Ajax&Axios
什么是ajax技术?
简单点说,就是使用AJAX 是异步的 JavaScript和XML(Asynchronous JavaScript And XML)。XMLHttpRequest 对象与服务器通信。
它可以使用 JSON,XML,HTML 和 text 文本等格式发送和接收数据。
AJAX 最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。
ajax是浏览器和服务器进行通信的技术
使用axios库
引入axios的库
https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
使用axios函数
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>axios</title> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> </head> <body> <p class="my-p"></p> <script> axios({ url:"http://hmajax.itheima.net/api/province" }).then((res) => { document.querySelector(".my-p").innerHTML = res.data.list.join('<br>') }) </script> </body> </html>
|
查询参数
在配置对象里添加一个params参数,配置对应的参数名和参数的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>axios</title> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> </head> <body> <p class="my-p"></p> <script> axios({ url:"http://hmajax.itheima.net/api/city", params:{ pname:'江西省' } }).then((res) => { document.querySelector(".my-p").innerHTML = res.data.list.join('<br>') }) </script> </body> </html>
|
常用的请求方式
请求方式 |
使用场景 |
post |
提交数据到服务器 |
delete |
删除服务器数据 |
put |
修改服务器数据(全部) |
get |
获取服务器数据 |
patch |
修改服务器数据(部分) |
axios请求配置
配置项 |
配置项含义 |
url |
请求的url网址 |
method |
请求方式 |
params |
拼接到url上的查询参数 |
data |
提交数据 |
axios的错误处理
什么情况下会走catch的逻辑?
- .then的逻辑报错
- 网络请求的状态码是异常的(默认是200-300之间走then,自定义状态码的情况需要具体分析)
在axios发请求代码后面添加.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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>上传文件</title> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"> </script> </head> <body> <input type="file" class="upload"> <img class="img" src="#" alt=""> <script> document.querySelector(".upload").addEventListener("change", (e) => { const fd = new FormData() fd.append("img", e.target.files[0]) axios({ url: 'http://hmajax.itheima.net/api/uploadimg', method: 'post', data: fd }).then((result) => { console.log(result.data.data.url) document.querySelector(".img").src = result.data.data.url }) }) </script> </body> </html>
|
ajax的底层原理
axios底层使用的也是XMLHttpRequest
使用步骤
- 创建XMLHttpRequest对象
- 配置请求方法和请求地址
- 监听loadend事件,接受响应结果
- 发起请求
基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>xhr</title> </head> <body> <div id="app">
</div> <script> var xhr = new XMLHttpRequest(); xhr.open("get", "http://hmajax.itheima.net/api/province") xhr.addEventListener("loadend", () => { console.log(xhr.response) const data = JSON.parse(xhr.response) document.querySelector("#app").innerHTML = data.list.join("<br>") }) xhr.send() </script> </body> </html>
|
添加查询参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>xhr</title> </head> <body> <div id="app">
</div> <script> var xhr = new XMLHttpRequest(); xhr.open("get", "http://hmajax.itheima.net/api/city?pname=江西省") xhr.addEventListener("loadend", () => { console.log(xhr.response) const data = JSON.parse(xhr.response) document.querySelector("#app").innerHTML = data.list.join("<br>") }) xhr.send() </script> </body> </html>
|
提交数据
核心操作是需要设置content-type请求头,标注上传的数据类型
发起请求时携带数据
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>xhr</title> </head> <body> <div id="app">
</div> <script> var xhr = new XMLHttpRequest(); xhr.open("post", "http://hmajax.itheima.net/api/register") xhr.setRequestHeader("Content-Type","application/json") xhr.addEventListener("loadend", () => { console.log(xhr.response) }) const user = { username:'test00122', password:'1234522' } let json = JSON.stringify(user) xhr.send(json) </script> </body> </html>
|
axios常见问题
如何配置接口的基地址?
1
| axios.defaults.baseURL = '地址'
|
axios请求拦截器
发起请求之前,触发的配置函数,可以在函数内编写公共操作
1 2 3 4 5 6 7 8 9 10
| axios.interceptors.request.use((config) => { let token = localStorage.getItem('token') token && (config.headers.Authorization = `Bearer ${token}`) return config }, (error) => { return Promise.reject(error) })
|
axios响应拦截器
响应回到.then,.catch之前,触发的配置函数,对响应结果统一处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| axios.interceptors.response.use((response) => { return response }, (error) => { if(error?.response?.status === 401){ alert("登录状态过期,请重新登录") localStorage.clear() location.href = '../login/index.html' } return Promise.reject(error) })
|
初识Promise
Promise对象用于表示一个异步操作的最终完成(失败)及其结果值
使用Promise函数的好处
- 让代码逻辑更加清晰
- 解决回调函数调用层级过多的问题
Promise函数的基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const promise = new Promise((resolve, reject) => { })
promise.then((result) => { console.log(result) }).catch((result) => { console.log(result) })
|
promise的三种状态
- 待定状态:初始状态,既没有兑现,也没有拒绝
- 已兑现:意味着操作成功完成
- 已拒绝:意味着操作失败
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>promise</title> </head> <body>
<div id="app">
</div>
<script> // 1.创建一个promise对象 const promise = new Promise((resolve, reject) => { var xhr = new XMLHttpRequest(); xhr.open('get','http://hmajax.itheima.net/api/province') xhr.addEventListener("loadend",() => { var json = JSON.parse(xhr.response); if(xhr.status >= 200 && xhr.status < 300){ resolve(json) } else { reject(json) } }) xhr.send() })
promise.then((result) => { // 处理成功的逻辑 let app = document.querySelector("#app") app.innerHTML = result.list.join('<br>') }).catch((result) => { // 处理失败的逻辑 console.log(result.message) }) </script> </body> </html>
|
封装一个简易版的myAxios
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>myAxios</title> </head> <body> <script> function myAxios(config) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); let url = config.url let method = config.method ? config.method : 'get' let params = config.params if(params){ let paramStr = new URLSearchParams(params).toString() if(url.indexOf('?') !== -1){ url += `&${paramStr}` } else { url += `?${paramStr}` } } xhr.open(method,url) xhr.addEventListener('loadend',()=>{ if(xhr.status >= 200 && xhr.status < 300){ resolve(xhr.response) } else { reject(new Error(xhr.response)) } }) let data = config.data if(data){ xhr.setRequestHeader("Content-Type",'application/json') let dataStr = JSON.stringify(data) xhr.send(dataStr) } else { xhr.send() } }) }
myAxios({ url:'http://hmajax.itheima.net/api/register', method:'post', data:{ username:'200000123123', password:'123123123123123123' } }).then(result => { console.log(result) }) </script> </body> </html>
|
同步代码和异步代码
同步代码:逐行执行,需要原地等待处理结果,才能继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行,在将来完成后触发一个回调函数
回调函数地狱
在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
存在代码可读性差,耦合性严重,异常无法捕获的问题。
异常无法捕获的现象是,外层的catch无法捕获到内层函数抛出的异常
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"> <title>链式调用</title> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"> </script> </head> <body> <script> let province = '' axios({ url: 'http://hmajax.itheima.net/api/province', method: 'get' }).then(result => { province = result.data.list[2] return axios({ url: 'http://hmajax.itheima.net/api/city', method: 'get', params: { pname: province } }) }).then(result => { return axios({ url: 'http://hmajax.itheima.net/api/area', method: 'get', params: { pname: province, cname: result.data.list[2] } }) }).then(result => { console.log("result",result) }).catch(error => { console.log("这里处理异常") }) </script> </body> </html>
|
aysnc & await
async 函数是使用 async 关键字声明的函数。async 函数是 AsyncFunction构造函数的实例,并且其中允许使用 await 关键字。
async 和 await 关键字让我们可以用一种更简洁的方式写出基于 promise 的异步行为,而无需刻意地链式调用 promise。
用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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>async&await</title> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"> </script> </head> <body> <script> async function getData() { const provinces = await axios({ url: 'http://hmajax.itheima.net/api/province', method: 'get' })
const cities = await axios({ url: 'http://hmajax.itheima.net/api/city', method: 'get', params: { pname: provinces.data.list[2] } })
const areas = await axios({ url: 'http://hmajax.itheima.net/api/area', method: 'get', params: { pname: provinces.data.list[2], cname: cities.data.list[2] } })
console.log(areas) }
getData() </script> </body> </html>
|
捕获异常处理
使用try…catch捕获。
1 2 3 4 5 6
| try { } 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
| async function getData() { try { const provinces = await axios({ url: 'http://hmajax.itheima.net/api/province', method: 'get' })
const cities = await axios({ url: 'http://hmajax.itheima.net/api/city', method: 'get', params: { pname: provinces.data.list[2] } })
const areas = await axios({ url: 'http://hmajax.itheima.net/api/area1', method: 'get', params: { pname: provinces.data.list[2], cname: cities.data.list[2] } })
console.log(areas) } catch (e) { console.log(e) } }
|
打印异常:
事件循环(EventLoop)
JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。
这个模型与其它语言中的模型截然不同,比如 C和 Java。
为什么JS要这样设计呢?
JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
事件循环执行过程
执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
- 将console.log(1)推入到调用栈,执行完出栈
- 将0秒的定时器推入调用栈,发现是异步任务,交给宿主环境,又因为是0秒后执行,将任务交给任务队列
- 将console.log(3)推入到调用栈,执行完出栈
- 将2秒的定时器推入调用栈,发现是异步任务,交给宿主环境,开始倒计时
- 将console.log(5)推入到调用栈,执行完出栈
- 代码执行完毕,调用栈空闲,尝试从任务队列取出任务到调用栈,此时0秒的任务被被推入调用栈,执行完毕出栈
- 2秒的倒计时完毕,任务进入任务队列
- 代码执行完毕,调用栈空闲,尝试从任务队列取出任务到调用栈,此时2秒的任务被被推入调用栈,执行完毕出栈
总结:Js的代码如何执行
- 执行同步代码,遇到异步代码交给宿主环境执行
- 异步有了结果之后,将回调函数交给任务队列等待排队
- 当调用栈空闲后,反复调用任务队列的回调函数
宏任务和微任务
宏任务和微任务的定义
- 宏任务:交给浏览器执行的异步代码
- 微任务:交给JS引擎执行的异步代码
宏任务和微任务的执行顺序
- 将console.log(1)推入到调用栈,执行完出栈
- 将0秒的定时器推入调用栈,发现是异步任务,交给宿主环境,又因为是0秒后执行,将任务交给宏任务队列
- 将创建promise对象的代码推入调用栈,因为promise本身是同步的,将3打印
- 将p.then代码推入调用栈,发现是异步任务,又因为Promise函数的then,catch是属于微任务,将这个回调函数交给微任务队列
- 将console.log(5)推入到调用栈,执行完出栈
- 调用栈空闲,优先从微任务队列取出任务执行,打印4
- 从宏任务队列取出任务执行,打印2
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"> <title>天气</title> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"> </script> </head> <body> <p class="my-p">
</p> <script> const arr = ['110100', '310100', '440100', '440300'] const promises = [] arr.forEach((item) => { promises.push( axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: item } }) ) }) let p = Promise.all(promises) p.then(result => { let ele = result.map(item => { return `<li>${item.data.data.area}---${item.data.data.weather}</li>` }).join('<br>') document.querySelector(".my-p").innerHTML = ele }).catch(error => { console.dir(error) }) </script> </body> </html>
|