Ajax学习笔记

cnbigmx 萌新

AJAX

  • 使用浏览器的 XMLHttpRequest 对象 与服务器通信
  • 浏览器网页中,使用 AJAX技术(XHR对象)发起获取省份列表数据的请求,服务器代码响应准备好的省份列表数据给前端,前端拿到数据数组以后,展示到网页

这里使用一个第三方库叫 axios, 后续在学习 XMLHttpRequest 对象了解 AJAX 底层原理.因为 axios 库语法简单,让我们有更多精力关注在与服务器通信上,而且后续 Vue,React 学习中,也使用 axios 库与服务器通信

基本语法

url:目标资源地址,method:请求方法,params:查询参数,data:提交的数据. 请求方法:GET,POST,PUT,DELETE,PATCH

无参GET请求

1
2
3
4
5
6
7
8
 <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
axios({
url: '目标资源地址'//默认get请求
}).then((result) => {
// 对服务器返回的数据做后续处理
})
</script>

有参GEt请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
document.querySelector('.sel-btn').addEventListener('click', () => {
let pname = document.querySelector('.province').value
let cname = document.querySelector('.city').value
axios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname,
cname
}
}).then(result => {
let list = result.data.list
console.log(list)
let theLi = list.map(areaName => `<li class="list-group-item">${areaName}</li>`).join('')
console.log(theLi)
document.querySelector('.list-group').innerHTML = theLi
})
})
</script>

在 url 网址后面用?拼接格式:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2.参数名一般是后端规定的,值前端看情况传递即可

POST请求

1
2
3
4
5
6
7
8
9
10
11
document.querySelector('.btn').addEventListener('click', () => {
axios({
url: 'http://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: 'itheima007',
password: '7654321'
}
})
})
// 请求会以json格式传递给后端 {"username":"itheima007","password":"7654321"}

axios错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
document.querySelector('.btn').addEventListener('click', () => {
axios({
url: 'http://hmajax.itheima.net/api/register',
method: 'post',
data: {
username: 'itheima007',
password: '7654321'
}
}).then(result => {
// 成功
console.log(result)
}).catch(error => {
// 失败
// 处理错误信息
console.log(error)
console.log(error.response.data.message)
alert(error.response.data.message)
})
})

文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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> 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)
// 取出图片url网址,用img标签加载显示
const imgUrl = result.data.data.url
document.querySelector('.my-img').src = imgUrl
})
})
</script>

async await

  1. 概念:在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
  2. 做法:使用 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
 /**
* 目标:async和await_错误捕获
* 目标:掌握async和await语法,解决回调函数地狱
* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
*/
async function getData() {
// 1. 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/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
} catch (error) {
// 2. 接着调用catch块,接收错误信息
// 如果try里某行代码报错后,try中剩余的代码不会执行了
console.dir(error)
}
}

getData()

请求报文

浏览器按照协议规定发送给服务器的内容

  • 请求行:请求方法,URL,协议
  • 请求头:以键值对的格式携带的附加信息,比如:Content-Type(指定了本次传递的内容类型)
  • 空行:分割请求头,空行之后的是发送给服务器的资源
  • 请求体:发送的资源

响应报文

服务器按照协议固定的格式,返回给浏览器的内容

  • 响应行(状态行):协议,HTTP响应状态码,状态信息
  • 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
  • 空行:分割响应头,控制之后的是服务器返回的资源
  • 响应体:返回的资源

HTTP响应状态码: 1xx 信息,2xx 成功,3xx 重定向消息,4xx 客户端错误,5xx 服务端错误.

AJAX 原理-XMLHttpRequest

XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequestAJAX 编程中被大量使用。

语法

1
2
3
4
5
6
7
8
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
// 响应结果
console.log(xhr.response)
})
// 发送请求
xhr.send()

POST传参请求

  1. 注意1:但是这次没有 axios 帮我们了,我们需要自己设置请求头 Content-Type:application/json,来告诉服务器端,我们发过去的内容类型是 JSON 字符串,让他转成对应数据结构取值使用

  2. 注意2:没有 axios 了,我们前端要传递的请求体数据,也没人帮我把 JS 对象转成 JSON 字符串了,需要我们自己转换

  3. 注意3:原生 XHR 需要在 send 方法调用时,传入请求体携带

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
document.querySelector('.reg-btn').addEventListener('click', () => {
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://hmajax.itheima.net/api/register')
xhr.addEventListener('loadend', () => {
console.log(xhr.response)
})

// 设置请求头-告诉服务器内容类型(JSON字符串)
xhr.setRequestHeader('Content-Type', 'application/json')
// 准备提交的数据
const userObj = {
username: 'itheima007',
password: '7654321'
}
const userStr = JSON.stringify(userObj)
// 设置请求体,发起请求
xhr.send(userStr)
})

Promise

Promise 对象用于表示一个异步操作的最终完成(或失败)及其结构值

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
// 2. 执行异步任务-并传递结果
// 成功调用: resolve(值) 触发 then() 执行
// 失败调用: reject(值) 触发 catch() 执行
})
// 3. 接收结果
p.then(result => {
// 成功
}).catch(error => {
// 失败
})

Promise.all

合并多个Promise 对象,等待所有同时成功完成(或某一个失败),做后续处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1. 请求城市天气,得到Promise对象
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' } })

// 2. 使用Promise.all,合并多个Promise对象
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
p.then(result => {
// 注意:结果数组顺序和合并时顺序是一致
console.log(result)
const htmlStr = result.map(item => {
return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`
}).join('')
document.querySelector('.my-ul').innerHTML = htmlStr
}).catch(error => {
console.dir(error)
})

模拟axios原理

XHR和Promise默认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
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))
}
})
// 1. 判断有data选项,携带请求体
if (config.data) {
// 2. 转换数据类型,在send中发送
const jsonStr = JSON.stringify(config.data)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(jsonStr)
} else {
// 如果没有请求体数据,正常的发起请求
xhr.send()
}
})
}

document.querySelector('.reg-btn').addEventListener('click', () => {
// 3. 使用myAxios函数,完成注册用户
myAxios({
url: 'http://hmajax.itheima.net/api/register',
method: 'POST',
data: {
username: 'itheima999',
password: '666666'
}
}).then(result => {
console.log(result)
}).catch(error => {
console.dir(error)
})
})

同步代码和异步代码

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

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

axios 全局拦截

请求拦截

1
2
3
4
5
6
7
8
9
10
11
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 统一携带 token 令牌字符串在请求头上
const token = localStorage.getItem('token')
token && (config.headers.Authorization = `Bearer ${token}`)
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});

响应拦截

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

URL

  1. 什么是 URL ?

    • 统一资源定位符,简称网址,用于定位网络中的资源(资源指的 是:网页,图片,数据,视频,音频等等)
  2. URL 的组成?

    • 协议,域名,资源路径(URL 组成有很多部分,我们先掌握这3个重要的部分即可)
  3. 什么是 http 协议 ?

    • 叫超文本传输协议,规定了浏览器和服务器传递数据的格式(而格式具体有哪些稍后我们就会学到)
  4. 什么是域名 ?

    • 标记服务器在互联网当中的方位,网络中有很多服务器,你想访问哪一台,就需要知道它的域名才可以
  5. 什么是资源路径 ?

    • 一个服务器内有多个资源,用于标识你要访问的资源具体的位置。