Ajax 基础

Ajax 定义

官网:https://www.axios-http.cn/

定义:AJAX 是异步的JavaScript和XML(Asynchronous JavaScript And XML)。简单点说,就是使用XMLHttpRequest 对象与服务器通信。它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。AJAX最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。

概念:Ajax是浏览器和服务器进行数据通信的技术

Ajax 使用

axios

  1. 语法
1
2
3
4
5
6
// 传入配置对象,再用.then回调函数接收结果,并做后续处理
axios({
url: '目标资源地址'
}).then((result) => {
// 对服务器返回的数据做后续处理
})

URL

定义

统一资源定位符,俗称网页地址,简称网址,是因特网上标准的资源的地址

组成

URL由协议,域名和资源路径组成

  1. 协议

http协议:超文本传输协议,规定浏览器和服务器之间传输数据的格式

  1. 域名

域名:标记服务器在互联网中方位

  1. 资源路径

资源路径:标记资源在服务器下的具体位置

查询参数

定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据

语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2

axios 查询参数

语法:使用axios提供的params选项

注意:axios在运行时把参数名和值,会拼接到url?参数名=值

1
2
3
4
5
6
7
8
axios({
url: '目标资源地址',
params: {
参数名: 值
}
}).then(result => {
// 对服务器返回的数据做后续处理
})

常用请求方法

请求方法 操作
GET 获取数据
POST 提交数据
PUT 修改数据(全部)
DELETE 删除数据
PATCH 修改数据(部分)

axios 请求配置

url:请求的 URL 网址

method:请求的方法,GET可以省略(不区分大小写)

data:提交数据

1
2
3
4
5
6
7
8
9
axios({
url: '目标资源地址',
method: '请求方法',
data: {
参数名: 值
}
}).then(result => {
// 对服务器返回的数据做后续处理
})

axios 处理错误信息

1
2
3
4
5
6
7
8
9
10
11
axios({
url: '目标资源地址',
method: '请求方法',
data: {
参数名: 值
}
}).then(result => {
// 对服务器返回的数据做后续处理
}).catch(error => {
// 对错误信息进行处理
})

HTTP 协议 - 请求报文

HTTP协议:规定了浏览器发送及服务器返回内容的格式

请求报文:浏览器按照HTTP协议要求的格式,发送给服务器的内容

请求报文的组成部分有:

  • 请求行:请求方法,URL,协议
  • 请求头:以键值对的格式携带的附加信息,比如:Content-Type
  • 空行:分隔请求头,空行之后的是发送给服务器的资源
  • 请求体:发送的资源

HTTP 协议 - 响应报文

HTTP协议:规定了浏览器发送及服务器返回内容的格式

响应报文:服务器按照HTTP协议要求的格式,返回给浏览器的内容

  • 响应行(状态行):协议、HTTP响应状态码、状态信息
  • 响应头:以键值对的格式携带的附加信息,比如:Content-Type
  • 空行:分隔响应头,空行之后的是服务器返回的资源
  • 响应体:返回的资源

HTTP 响应状态码

HTTP 响应状态码:用来表明请求是否成功完成

比如:404(服务器找不到资源)

状态码 说明
1xx 信息
2xx 成功
3xx 重定向消息
4xx 客服端错误
5xx 服务端错误

接口文档

接口文档:描述接口的文章(后端工程师)

接口:使用Ajax和服务器通讯时,使用的 URL,请求方法,以及参数

form-serialize插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>13.form-serialize插件使用</title>
</head>

<body>
<form action="javascript:;" class="example-form">
<input type="text" name="username">
<br>
<input type="text" name="password">
<br>
<input type="button" class="btn" value="提交">
</form>
<!--
目标:在点击提交时,使用form-serialize插件,快速收集表单元素值
-->
<!-- 1.本地引入form-serialize插件 -->
<script src="./lib/form-serialize.js"></script>
<script>
document.querySelector('.btn').addEventListener('click', () => {
/*
* 2. 使用serialize函数,快速收集表单元素的值
参数1:要获取哪个表单的数据
表单元素设置name属性,值会作为对象的属性名
建议name属性的值,最好和接口文档参数名一致
参数2:配置对象
hash 设置获取数据结构
- true:JS对象(推荐)一般请求体里提交给服务器
- false:查询字符串
empty 设置是否获取空值
- true:获取空值(推荐)数据结构和标签结构一致
- false:不获取空值
*/
const form = document.querySelector('.example-form')
const data = serialize(form, {hash: true, empty: true})
console.log(data)
})
</script>
</body>

</html>

图片上传

  1. 获取图片文件对象
  2. 使用 FormData 携带图片文件
1
2
const fd = new FormData()
fd.append(参数名, 值)
  1. 提交表单数据到服务器,使用图片url网址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片上传</title>
</head>

<body>
<!-- 文件选择元素 -->
<input type="file" class="upload">
<img src="" alt="" class="my-img">

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:图片上传,显示到网页上
* 1. 获取图片文件
* 2. 使用 FormData 携带图片文件
* 3. 提交到服务器,获取图片url网址使用
*/
// 给input绑定事件
document.querySelector('.upload').addEventListener('change', e => {
// 1. 获取图片文件
console.log(e.target.files[0])
// 2. 使用 FormData 携带图片文件
const fd = new FormData()
fd.append('img', e.target.files[0])
// 3. 提交到服务器,获取图片url网址使用
axios({
url: 'http://hmajax.itheima.net/api/uploadimg',
method: 'post',
data: fd
}).then(result => {
console.log(result.data.data.url)
const imgUrl = result.data.data.url
document.querySelector('.my-img').src = imgUrl
})
})
</script>
</body>

</html>

Ajax 原理

XMLHttpRequest(XHR)

  1. 定义:XMLHttpRequest(XHR)对象用于与服务器交互。通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX 编程中被大量使用。
  2. 关系:axios内部采用XMLHttpRequest与服务器交互
  1. 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 创建XMLHttpRequest 对象
const xhr = new XMLHttpRequest()

// 2.配置请求方法和请求url 地址
xhr.open('请求方法', '请求url网址')

// 3.监听loadend 事件,接收响应结果
xhr.addEventListener('loadend', () => {
// 响应结果
console.log(xhr.reponse)
})

// 4.发起请求
xhr.send()
  1. 查询参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 创建XMLHttpRequest 对象
const xhr = new XMLHttpRequest()

// 2.配置请求方法和请求url 地址
xhr.open('请求方法', '请求url网址?参数1=值1&参数2=值2')

// 3.监听loadend 事件,接收响应结果
xhr.addEventListener('loadend', () => {
// 响应结果
console.log(xhr.reponse)
})

// 4.发起请求
xhr.send()
  1. URLSearchParams 对象
1
2
3
4
5
6
7
8
9
// 1.创建URLSearchParams 对象
const paramsObj = new URLSearchParams({
参数名1: 值1,
参数名2: 值2
})

// 2.生成指定格式查询参数 字符串
const queryString = paramsObj.toString()
// 结果: 参数名1=值1&参数名2=值2

Promise

  1. 定义:Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值
  2. 语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1.创建Promise对象
const p = new Promise((resolve, reject) => {
// 2.执行异步任务-并传递结果
// 成功调用:resolve(值)触发then()执行
// 失败调用: reject(值) 触发catch()执行
})
// 3.接收结果

p.then(result => {
// 成功

}).catch(error => {
// 失败

})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>认识Promise</title>
</head>

<body>
<script>
/**
* 目标:使用Promise管理异步任务
*/
// 1.创建Promise对象
const p = new Promise((resolve, reject) => {
// 2.执行异步代码
setTimeout(() => {
resolve('模拟Ajax成功-成功结果')
reject(new Error('模拟Ajax失败-失败结果'))
}, 2000)
})

// 3.获取结果
p.then(result => {
alert(result)
}).catch(result => {
alert(result)
})
</script>
</body>

</html>
  1. 三种状态

概念:一个Promise对象,必然处于以下几种状态之一

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝
  • 已兑现(fulfilled):意味着,操作成功完成
  • 已拒绝(rejected):意味着,操作失败

注意:Promise对象一旦被兑现/拒绝就是已经敲定了,状态无法在被改变

封装简易Axios函数

  1. 封装不包含查询参数和请求体的Axios函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>封装_简易axios函数_获取省份列表</title>
</head>

<body>
<p class="my-p"></p>
<script>
/**
* 目标:封装_简易axios函数_获取省份列表
* 1. 定义myAxios函数,接收配置对象,返回Promise对象
* 2. 发起XHR请求,默认请求方法为GET
* 3. 调用成功/失败的处理程序
* 4. 使用myAxios函数,获取省份列表展示
*/
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
// 后端返回的状态码在200到300之间,则执行成功的逻辑
// 否则执行错误的逻辑
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
// 发送请求
xhr.send()
})
}

// 调用函数
myAxios({
url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
console.dir(error)
document.querySelector('.my-p').innerHTML = error.message
})
</script>
</body>

</html>
  1. 封装不包含请求体的Axios函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>封装_简易axios函数_获取地区列表</title>
</head>

<body>
<p class="my-p"></p>
<script>
/**
* 目标:封装_简易axios函数_获取地区列表
* 1. 判断有params选项,携带查询参数
* 2. 使用URLSearchParams转换,并携带到url上
* 3. 使用myAxios函数,获取地区列表
*/
function myAxios(config) {
return new Promise((resolve, reject) => {
// 如果请求参数存在,则通过URLSearchParams对象将对象参数转为字符串类型
if (config.params) {
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
config.url += `?${queryString}`
}
const xhr = new XMLHttpRequest()
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
}

myAxios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname: '四川省',
cname: '南充市'
}
}).then(result => {
console.log(result)
}).catch(error => {
console.dir(error)
})
</script>
</body>

</html>
  1. 封装带有核心功能的Axios函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>封装_简易axios函数_注册用户</title>
</head>

<body>
<button class="reg-btn">注册用户</button>
<script>
/**
* 目标:封装_简易axios函数_注册用户
* 1. 判断有data选项,携带请求体
* 2. 转换数据类型,在send中发送
* 3. 使用myAxios函数,完成注册用户
*/
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()

if (config.params) {
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
config.url += `?${queryString}`
}
xhr.open(config.method || 'GET', config.url)

xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})

// 如果config.data存在,则将请求体的数据发送给后端
if (config.data) {
const dataJson = JSON.stringify(config.data)
xhr.setRequestHeader('content-type', 'application/json')
xhr.send(dataJson)
} else {
xhr.send()
}
})
}

document.querySelector('.reg-btn').addEventListener('click', () => {
myAxios({
url: 'http://hmajax.itheima.net/api/register',
method: 'post',
data: {
username: 'aoshudingli',
password: '123456'
}
}).then(result => {
console.log(result)
}).catch(error => {
// console.dir()打印详细错误信息
console.dir(error)
})
})
</script>
</body>

</html>

Ajax 进阶

Axios 实例

当项目中会有多个请求,并且每个请求的基地址等等基础配置不同,那么可以通过 axios 实例来区分不同的请求

1
2
3
4
5
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});

同步代码和异步代码

同步代码:逐行执行,需原地等待结果后,才继续向下执行

异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数

回调函数地狱

概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱

缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>回调地狱</title>
</head>

<body>
<form>
<span>省份:</span>
<select>
<option class="province"></option>
</select>
<span>城市:</span>
<select>
<option class="city"></option>
</select>
<span>地区:</span>
<select>
<option class="area"></option>
</select>
</form>
<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:演示回调函数地狱
* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
* 概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
* 缺点:可读性差,异常无法获取,耦合性严重,牵一发动全身
*/
// 1.默认获取第一个省份
axios({ url: 'http://hmajax.itheima.net/api/province'}).then(result => {
const pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 2.默认获取第一个城市
axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname }}).then(result =>{
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 3.默认获取第一个地区
axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }}).then(result => {
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
})
})
}).catch(error => {
// 无法捕获内部的异常
console.dir(error)
})
</script>
</body>

</html>

Promise - 链式调用

概念:依靠then()方法会返回一个新生成的Promise对象特性,继续串联下一环任务,直到结束

细节:then()回调函数中的返回值,会影响新生成的Promise对象最终状态和结果

好处:通过链式调用,解决回调函数嵌套问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise_链式调用</title>
</head>

<body>
<script>
/**
* 目标:掌握Promise的链式调用
* 需求:把省市的嵌套结构,改成链式调用的线性结构
*/
const p = new Promise((resolve, reject) => {
// 1.模拟获取第一个城市名
setTimeout(() => {
resolve('北京市')
}, 2000)
})

// 异步接收
const p2 = p.then(result => {
console.log(result)
return new Promise((resolve, reject) => {
// 2.模拟获取第一个城市
setTimeout(() => {
resolve('东城区')
})
})
})

p2.then(result => {
console.log(result)
})

</script>
</body>

</html>

使用Promise的链式调用解决回调函数地狱问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise链式调用_解决回调地狱</title>
</head>

<body>
<form>
<span>省份:</span>
<select>
<option class="province"></option>
</select>
<span>城市:</span>
<select>
<option class="city"></option>
</select>
<span>地区:</span>
<select>
<option class="area"></option>
</select>
</form>
<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:把回调函数嵌套代码,改成Promise链式调用结构
* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
*/
// 将省份名设置成全局变量
let pname
// 1.默认获取第一个省份数据
axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 2.默认获取第一个城市数据
return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname }})
}).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 3.默认返回第一个地区数据
return axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
}).then(result => {
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
})
</script>
</body>

</html>

Promise.all 静态方法

概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise的all方法</title>
</head>

<body>
<ul class="my-ul"></ul>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:掌握Promise的all方法作用,和使用场景
* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
* code:
* 北京-110100
* 上海-310100
* 广州-440100
* 深圳-440300
*/
const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100'} })
const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100'} })
const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100'} })
const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300'} })

const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
console.log(result)
const weatherStr = result.map(item => `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`).join('')
document.querySelector('.my-ul').innerHTML = weatherStr
}).catch(error => {
console.dir(error)
})
</script>
</body>

</html>

async函数和await

  1. 定义

async函数是使用async关键字声明的函数。async函数是 AsyncFunction构造函数的实例,并且其中允许使用 await 关键字。async 和 await关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。

  1. 使用async函数和await关键字解决回调函数地狱的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>async函数和await_解决回调函数地狱</title>
</head>

<body>
<form>
<span>省份:</span>
<select>
<option class="province"></option>
</select>
<span>城市:</span>
<select>
<option class="city"></option>
</select>
<span>地区:</span>
<select>
<option class="area"></option>
</select>
</form>
<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:掌握async和await语法,解决回调函数地狱
* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
*/
// async 关键字将函数改造成异步执行的函数
async function getData() {
// 使用await关键字,获取Promise对象"成功状态"结果值
const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
const pname = pObj.data.list[0]
const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })
const cname = cObj.data.list[0]
const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } })
const areaName = aObj.data.list[0]

document.querySelector('.province').innerHTML = pname
document.querySelector('.city').innerHTML = cname
document.querySelector('.area').innerHTML = areaName
}
getData()
</script>
</body>

</html>
  1. 使用try….catch语句块获取异常信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>async函数和await_错误捕获</title>
</head>

<body>
<form>
<span>省份:</span>
<select>
<option class="province"></option>
</select>
<span>城市:</span>
<select>
<option class="city"></option>
</select>
<span>地区:</span>
<select>
<option class="area"></option>
</select>
</form>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:async和await_错误捕获
*/
async function getData() {
// 使用try包裹可能出现错误的代码
try {
const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
const pname = pObj.data.list[0]
const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } })
const cname = cObj.data.list[0]
const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area1', params: { pname, cname } })
const areaName = aObj.data.list[0]

document.querySelector('.province').innerHTML = pname
document.querySelector('.city').innerHTML = cname
document.querySelector('.area').innerHTML = areaName
} catch (error) {
// 使用catch捕获异常信息
// try中出现异常的某行代码后的代码都不会继续执行
console.dir(error)
}
}

getData()
</script>
</body>

</html>

事件循环

  1. 概念:JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如C和Java。
  2. 原因:JavaScript 是单线程语言(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
  3. JavaScript 内代码执行流程
  • 执行同步代码,遇到异步代码交给宿主浏览器环境执行
  • 异步有了结果后,把回到函数放入任务队列排队
  • 当调用栈空闲后,反复调用任务队列里的回调函数

JavaScript 内代码执行流程视频演示:https://www.bilibili.com/video/BV1MN411y7pw?spm_id_from=333.788.player.switch&vd_source=4ad0a2f2ad6c40766c4bfa7c008aaa3f&p=53

宏任务和微任务

ES6 之后引入了 Promise 对象,让JS引擎也可以发起异步任务

  1. 宏任务:由浏览器环境执行的异步代码
  2. 微任务:由JS引擎环境执行的异步代码
任务(代码) 执行所在环境
JS脚本执行事件(script) 浏览器
setTimeout/setInterval 浏览器
Ajax请求完成事件 浏览器
用户交互事件等 浏览器
Promise对象.then() JS 引擎
  1. 执行顺序:
  • 执行栈中先执行同步代码和Promise中的同步代码(Promise对象中只有then和catch函数中的回调函数才是异步代码)
  • 遇到宏任务/微任务 交给宿主环境,有结果回调函数则放入相应队列中
  • 当执行栈空闲时,先执行微任务队列,后执行宏任务队列

Axios - 配置默认参数

配置默认请求前缀

1
axios.defaults.baseURL = '默认请求前缀'

Axios 请求拦截器

axios 请求拦截器: 发起请求之前,触发的配置函数,对请求参数进行额外配置

1
2
3
4
5
6
7
8
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});

Axios 响应拦截器

axios 响应拦截器:响应回到then/catch之前,触发的拦截函数,对响应结果统一处理

例如:身份验证失败,统一判断并做处理

1
2
3
4
5
6
7
8
9
10
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});

Axios 优化响应结果

在响应拦截器中的修改成功时的返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 添加响应拦截器
axios. interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 返回响应的数据结果,而非所有结果
const result = response.data
return result;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么,例如:判断响应状态为 401 代表身份验证失败
if (error ?. response ?. status === 401) {
alert('登录状态过期,请重新登录')
localStorage.clear ()
location.href =' .. /login/index.html'
}
return Promise.reject(error);
});

wangeditor - 富文本编辑器

富文本:带样式,多格式的文本,在前端一般使用标签配合内联样式实现

富文本编辑器:用于编写富文本内容的容器

官方网址:https://www.wangeditor.com/