Event Loop
js是单线程的,所有任务都在主线程上执行,形成一个执行栈,主线程外还存在一个任务队列,Event Loop是解决异步回调的一种机制,遇到同步任务时,直接压入执行栈执行,遇到异步任务,宏任务和微任务时会放入相应的队列中,当同步任务执行完毕,eventloop检查到了就会按顺序压入执行栈执行,执行完后开始渲染,然后不断重复。
浏览器输入URL后发生的事情
- URL检查 :先进行url的合法检查
- 合法 -> 检查完整度,不完整浏览器可能会对地址进行猜测,然后补齐前缀或后缀;
- 不合法 -> 将内容作为搜索条件,使用默认搜索引擎进行搜索。
- DNS解析域名 :操作系统会先检查
浏览器缓存
和本地的hosts
中是否有该网站的记录- 有 -> 直接从记录里找对应IP地址完成域名解析
- 没有 -> 使用TCP/IP参数中设置的DNS服务器进行查询
- 如果查询的域名包含在
本地配置区域
则直接返回解析结果,完成域名解析。 - 没有的话检查
本地DNS服务器
是否缓存有该网站的记录,有的话返回解析结果,完成域名解析。- 还没有的话
本地DNS 服务器
会发送查询报文到根DNS服务器
,根DNS 服务器
收到请求后返回顶级域DNS服务器
地址,本地DNS 服务器
再发查询报文到顶级域DNS服务器
,顶级域DNS服务器
收到请求后,返回权威DNS 服务器
的地址,本地DNS服务器
再发送查询报文到权威DNS服务器
,权威DNS服务器
收到后返回最终的IP地址,完成域名解析。
- 还没有的话
- 如果查询的域名包含在
- 建立TCP连接 :当浏览器获取到服务器的IP地址后,浏览器会用一个
随机的端口号
向服务器80端口发送一个TCP连接请求
,这个请求到达服务器后,会通过TCP三次握手
建立TCP连接。 - 发送HTTP请求 :建立连接后就可以通过HTTP进行数据的传输,使用时HTPS会在TCP与HTTP直接
多一层协议
作为加密及认证
的服务。HTTPS使用SSL和TLS协议,保障了信息的安全,SSL 协议的作用是认证客户端和服务器
,确保数据发送到正确的客户端和服务器,加密数据,防止数据中途被窃取,维护数据的完整性,确保数据在传输过程中不会被改变。TLS协议的作用是用于在两个通信应用程序之间
,提供保密性和数据完整性。TLS协议由TLS记录协议和TLS 握手协议组成。 - 服务器响应请求 :当浏览器和服务器建立连接之后,浏览器会发送一个
初始的HTTP GET请求
,请求目标通常是一个HTML文件,服务器收到请求后发回一个HTTP响应报文
,内容包括相关的响应头和HTML正文。 - 渲染服务器:不同浏览器的渲染过程不太一样,谷歌是先处理HTML标记并构建DOM树,再处理CSS标记并构建CSS规则树,再将DOM树和CSS规则树合并构建一个Rander渲染树,根据渲染树来布局,以计算每个节点的几何信息,再绘制页面
- TCP断开连接:现在的浏览器页面为了优化请求的耗时都会默认开启持久连接,就是页面关闭TCP连接才会关闭,这个关闭的过程就是四次挥手。
TCP 的三次握手和四次挥手
TCP协议是传输层的一个面向连接的安全可靠的传输协议。
三次握手
三次握手的机制是为了保证能建立一个安全可靠的连接。
- 第一次握手:由客户端发起,客户端会发送一个请求报文(报文中SYN = 1)。
- 第二次握手:服务端收到这个报文后就知道客户端要发起一个新的连接,服务端就会发送一个确认消息的报文(ACK = 1),表示确认客户端发起的连接请求。
- 第三次握手:两次握手后客户端已经知道自己发送的请求可以到达服务端,且服务端发送的响应也可以到达客户端,但此时服务端并不知道自己的响应报文是否能送达客户端,所以客户端需要发送第三次握手(ACK = 1),给服务端一个回应 三次握手后两方就都能确定消息能送到也能收到,实现双工,这样就可以建立起一个安全的连接。
四次挥手
- 第一次挥手:客户端会发送一个请求报文(FIN = 1),服务器收到后就知道客户端要断开连接了。
- 第二次挥手:此时服务端不一定传完数据了,所以只能进行一个消息的确认而不是同意。
- 第三次挥手:此时服务端已经准备好断开连接了,所以会发送一个响应报文(FIN = 1)通知客户端我已经做好准备了
- 第四次挥手:此时服务端依然不知道报文是否送达客户端,所以客户端要发起一个消息确认的报文。 经过四次挥手能确认客户端和服务端都准备好断开连接了,这样就能安全的断开一个连接。
HTTP && HTTPS加密
以前都是用HTTP协议的,但是HTTP协议传输的数据是公开的,很容易被劫取导致数据泄露,非常不安全。
所以有了HTTPS协议,现在基本上用的都是HTTPS,它对请求报文和响应报文都做了加密,即使被劫取了也得不到具体的数据。
HTTPS加密的方式
- 对称加密
- 特点
- 加密和解密使用相同的密钥。
- 很高效,适合大量数据的加密场景。
- 算法公开,安全性取决于密钥大小,密钥越大效率越低。
- 缺点
- 算法本身安全,但使用的环境不安全,因为加密解密用的都是同一个密钥。
- 特点
- 非对称加密
- 特点
- 使用匹配的一对密钥 来分别进行加密和解密,这两个密钥是公开密钥 和私有密钥。
- 公钥加密的数据只能用对于的私钥解开,同理私钥加密的也只能公钥解开。
- 安全性高。
- 缺点
- 加密解密复杂、效率低、耗时长
- 特点
过程
初期
一开始是使用对称加密。 浏览器向服务器发送请求的时候,需要把密钥也一起发送给服务器,传输过程中如果有黑客的话,可以直接截取请求和密钥进行解密,非常不安全!
中期
所以出现了非对称加密。 用的公钥和私钥,但是发送请求的时候需要把公钥也一同发送过去,这又跟对称加密一样,遇到了同样的坑,黑客可以截取公钥,形成自己的黑客公钥传给浏览器,伪造成服务器发的,然后浏览器使用公钥对自己的数据进行加密然后传输,黑客就用自己的私钥进行解密,然后再用服务器给的公钥进行加密伪造成浏览器发的。
后期
最后出现了认证机构。
让浏览器有能力判断是否是服务器发送的公钥 。服务器将自己的公钥和域名发送给认证机构,认证机构也生成一个自己的公钥和密钥,用秘钥对服务器的公钥和域名进行加密生成服务器的证书 然后还给服务器,再将自己认证机构的公钥发给浏览器(用于解开服务器的证书)这样当浏览器需要向服务器发送请求时,可以先发送一个请求获取服务器证书 ,服务器将证书发过去,浏览器用认证机构的公钥进行解密,然后就能拿到服务器的公钥和域名了,然后下一步就是要发送请求报文了,但是如果用非对称加密再形成一个公钥和密钥,若有大量的数据会非常耗性能,这样就适得其反了。所以可以使用对称加密,将这里对称加密的密钥就称为会话密钥,但是不能直接会话密钥传输过去,不然还是可能被黑客拦截,这样前面的努力就都白费了。可以通过服务器的公钥将会话密钥加密 ,形成密文,传给服务器,然后服务器用私钥解密拿到会话密钥。这样浏览器就可以用会话密钥对请求报文进行加密安全传输过去,服务器也可以用会话密钥对响应报文进行加密,形成密文,安全传回去。
TCP和UDP
- TCP可以保证连接的稳定可靠性 ,他发送的时候会带一些状态标记和序列号,能确认双方都可以接收和发送消息 ,且能够处理丢包、保证数据包按序到达。
- UDP是无连接 的,也没有序列号,无法保证双方能正常获取数据,也不会对数据进行确认,就是效率比TCP高。
HTTP协议
超文本传输协议 ,规定了浏览器和万维网服务器之间相互通信的规则。
规定了请求报文和响应报文。
HTTP请求交互的基本过程
浏览器 -> 服务器 :1. 请求行 2. 请求头 3. 请求体
服务器 -> 浏览器: 1. 状态行 2. 响应头 3. 响应内容
请求报文
js
请求行 //1.请求类型 2.URL路径 3.HTTP协议版本
请求头
1.Host:
2.Connection:
3.Content-Type:
4.Content-Length:
...
空行
请求体 // 用户信息和资源信息等
//GET请求时请求体为空,POST请求时可以不为空
常用请求类型
js
GET //从服务器端读取数据
POST //向服务器添加新数据
PUT //更新服务端已有数据
DELETE //删除服务端数据
POST 请求参数格式
js
//1. 用于键值对参数,参数的键值用=连接,参数之间用&连接
Content-Type = application/x-www-form-urlencoded;charset-utf-8
name=cloud&age=21
//2. 用于JSON字符串
Content-Type: application/json;charset=utf-8
{
"name" : "cloud",
"age": 18
}
//3. 用于文件上传
Content-Type: multipart/form-data
响应报文
js
状态行 // 1.HTTP协议版本 2.状态码 3.状态码的原因语句(OK)
响应头部
1.Data
2.Content-Type:请求体的类型
3.Content-Length:
空行
体 // 服务端返回的资源信息(一般是html形式的)
常见状态码
js
200 --- OK 请求成功
201 --- Created 已创建,成功请求并创建新资源
401 --- Unauthorized 未授权,请求要求用户的身份认证
404 --- Not Found 服务器无法根据客户端的请求找到资源
500 --- Internal Server Error 服务器内部错误,无法完成请求
AJAX
Ajax是一种异步请求数据的web开发技术,是一个概念模型,是一种思想。它包含了多种技术,并不特指一种。
优点
- 可以在浏览器不刷新网页的情况下向服务器请求数据。
- 允许用户根据触发事件来更新部分页面内容。
缺点
- 没有浏览历史,无法回退。
- 存在跨域问题(必须同源)。
- SEO不友好。
- 同源策略:协议、域名、端口号必须一样
bash
http://cloud.com:8000
http协议 cloud.com 域名 8000端口号
违背同源策略就是跨域
区别一般HTTP请求和AJAX请求
-
AJAX是一种特别的HTTP请求。
-
对于服务器来说没有任何区别,区别在于浏览器端。
-
只有XHR或Fetch发出的才是AJAX请求,其他都是非AJAX请求。
-
浏览器收到响应
- 一般请求:浏览器一般会直接显示响应体数据,也就是刷新/跳转页面。
- AJAX请求:浏览器不对页面进行任何更新操作(不刷新),必须主动调用监视的回调函数并传入响应相关的数据。
XHR概念
使用XMLHttpRequest(XHR) 对象可以与服务器交互,也就是发送AJAX请求(实现AJAX思想),使得前端可以在不刷新页面的情况下获取数据,不影响用户的操作。
API
js
XMLHttpRequest(): 创建XHR对象的构造函数
status:响应状态码值,比如200 404
statusText:响应状态文本
readyState:标识请求状态的只读属性
0:初始
1:open()之后
2:send()之后
3:请求中
4:请求完成
open():初始化一个请求,参数('method', 'url' )
send(data):发送请求
abort():中断请求
onredaystatechange:绑定readyState改变的监听函数
responseType:指定响应数据的类型,如果`responseType='json'`,得到响应后的数据自动解析为JS
response:响应体数据,类型取决于responseType的指定
timeout:指定请求超时的时间,默认0代表没有限制
ontimeout:绑定请求超时的监听函数
getResponseHeader(name):获取指定名称的响应头值
getAllResponseHeaders():获取所有响应头组成的字符串
setRequestHeader(name, value):设置请求头
XHR发送AJAX请求
创建一个服务器
可以使用express
创建一个服务器端。express是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。 nodemon
是一个可以实时监听服务器端变化的插件,这样服务器端就不用改一次必须重启一次才生效。
bash
npm i express -g
npm i nodemon -g
js
//服务器端 server.js
const express = require('express');
//创建应用对象
const app = express();
//监听端口启动服务
app.listen(8000, () => {
console.log('服务器已开启...');
});
//使用nodemon server.js开开启并监听服务器
路由规则
js
//响应请求的路由规则
// app.xxx('响应url',(请求报文, 响应报文) => {})创建路由规则
//request 请求报文
//response 响应报文
app.get('/server', (request, response) => {
//设置响应头
//设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*');
//设置响应体
response.send('Hello GET');
})
app.post('/server', (request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
//设置允许自定义请求头
response.setHeader('Access-Control-Allow-Headers', '*');
response.send('Hello POST');
})
请求
js
function GET() {
//创建一个XHR对象
const xhr = XMLHttpRequest();
//初始化请求
xhr.open('GET', 'http://localhost:8000/server');
//发送请求
xhr.send();
//监听请求的状态
xhr.onreadystatechange = function () {
//当readystate为4时表示请求送达
if (xhr.readyState === 4) {
//当状态码[200, 300)表示请求成功(可以获得之前响应)
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
}
}
}
}
function POST() {
const xhr = XMLHttpRequest();
xhr.open('POST','http://localhost:8000/server');
//设置请求体的类型
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
//自定义请求头,在服务器端需要设置允许自定义
xhr.setRequestHeader('Name', 'cloud');
//POST可以发送请求体
//格式比较灵活,与服务器要对应
// xhr.send('a=100&b=200');
xhr.send('a:100&b:200');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
}
}
}
}
GET && POST
- 传输方式: GET是拼接在URL上传输的,POST是放在请求体里传输
- 安全性:GET请求的数据是通过URL传输的,这意味着所有的请求数据都会在浏览器的历史记录、服务器的日志、网络设备的日志中留下记录,这在某些情况下可能会泄露敏感信息。POST的安全性比GET高
- 历史记录&&缓存:GET是在URL上传播的,所以可以被缓存在历史记录中,还可以添加书签,而POST没有缓存记录所以不行
- 幂等性:幂等性是指无论一个操作进行一次或多次,结果都是相同的。GET是幂等的,POST不是,GET发送多次请求的请求结果都是相同的,而POST是创建一个数据,所以多次请求会创建多个相同数据
- 大小限制:因为GET是在URL上传输的,所以有大小限制,URL的长度限制为2000字符左右,所以发大量数据的时候用POST更好
- 数据类型:GET请求的数据类型通常是字符串,而POST是多样性的,例如JSON、XML、图片等资源
当响应数据为json时
json对象包含两个方法
json.parse()
是将JSON格式的字符串转换为 JS的对象或者数组json.stringify()
是将JS的对象或数组转换为JSON
js
//响应
app.all('/json-server', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Header'.'*');
const data = {
name: 'cloud'
}
//对响应对象转换为json格式
let str = JSON.stringify(data);
response.send(str);
})
//请求
function JSON() {
const xhr = new XMLHttpRequest();
//设置响应体的数据类型(可以自动转化json数据)
xhr.responseType = 'json';
xhr.open('GET', 'http://localhost:8000/json-server');
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
}
}
}
}
网络超时/异常
js
//设置定时器模拟网络超时
app.get('/delay', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
setTimeout(() => {
response.send('Hello delay');
}, 3000)
})
//请求
function delay() {
const xhr = new XMLHttpRequest();
//超时设置 在规定时间内未响应就取消请求(自动)
xhr.timeout = 2000;
//网络异常的回调
xhr.onerror = funtcion() {
alert('哎哟( ̄y▽, ̄)╭ 你的网咋这么不经用捏');
}
xhr.open('GET', 'http://localhost:8000/delay');
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
}
}
}
}
手动取消请求
js
app.get('/cancel', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
setTimeout(() => {
response.send('Hello Cancel');
}, 3000)
})
//请求
//xhr不止在一个函数里使用,所以得定义成全局
let xhr = null;
function send() {
xhr = new XMLHttpRequest();
xhr.open('GET','http://localhost:8000/cancel');
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4){
if (xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response);
}
}
}
}
function cancel() {
//手动取消请求
xhr.abort();
}
重复请求
js
app.get('/repeat', (request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
setTimeout(() => {
response.send('Hello Repeat');
}, 3000)
});
//请求
let xhr = null;
//设置一个标识变量
let isSending = false;// 是否正在发送中
function send() {
//先判断是否有正在发送的请求,若有则取消
if (isSending) xhr.abort();
xhr = new XMLHttpRequest();
//创建好一个请求就说明要发送,可以设置为true
isSending = true;
xhr.open('GET', 'http://localhost:8000/repeat');
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
//当状态码为4就表明发送成功了
//无论请求的得到的响应结果如何,只要为4,就表明服务器已经收到请求了,所以可以设为false
isSending = false;
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
}
}
}
}
XHR封装AJAX(低配axios)
特点
- 函数的返回值为promise,成功的结果为response,异常结果为error
- 能处理多种类型的请求: GET/POST/PUT/DELETE
- 函数的参数为一个配置对象:
js
{
url: '', // 请求地址
method:'', // 请求方式
params: {}, // 请求的query参数
data: {} // 请求的请求体参数
}
//参数传递的两种方式 params和data
//params是添加到url的请求字符串中的,用于GET请求。
//data是添加到请求体(body)中的, 用于POST请求。
- 响应json数据时自动解析为js
使用json-server扮演一个服务器
- 安装json-server
npm i json-server -g
- 新建
db.json
并存入数据
json
{
"posts": [
{
"title": "majo",
"autor": "goods",
"id": 1
},
{
"title": "甄嬛传",
"autor": "cloud",
"id": 5
}
],
}
- 启动服务器
json-server --watch db.json
- 接口规则-RESTful说明
接口地址 | 请求方式 | 适用情况 |
---|---|---|
/posts | GET | 查询所有数据 |
/posts/id | GET | 查询单条数据 |
/posts | POST | 添加数据 |
/posts/id | DELETE | 删除数据 |
/posts/id | PUT | 修改数据 |
实现
- 定义一个axios函数,函数的参数为基础配置
function axios(config) {}
,我们将它写成一个对象。
js
//url不给默认值,就是必须传,否则会报
//其余都给一个默认值
function axios ({
url,
method = 'GET',
parmas = {},
data = {}
}) {}
- 返回一个promise对象
js
function axios({
url,
method = 'GET',
parmas = {},
data = {}
}) {
return new Promise((resolve, reject) => {
//对method大小写的处理
//使用axios函数的时候,有可能会写post而不是POST
method = method.toUpperCase();//转换成大写
//params是拼接在url上的参数,所以需要对params进行处理
//创建一个变量用来保存转换后params的字符串
let queryString = '';
//因为params传进来的是一个对象,所以需要对里面的属性进行遍历
Object.keys(params).forEach(key => {
queryString += `${key}=${params[key]}&`;
})
//拼接完后结尾会多一个&需要删除
if (queryString) {
//subString(开始位置,结束位置) 可以截取出开始到结束位置的字符串
queryString = queryString.subString(0, queryString.length - 1) //截取出除了最后一位的所有内容
//最后拼接在url上
url += '?' + queryString;
}
//执行异步ajax请求
//1.创建xhr对象
const xhr = new XMLHttpRequest();
//2.初始化请求
xhr.open(method, url);
//3.发送请求
//判断发送的方法
if (method === 'GET' || method = 'DELETE')
//GET是不能设置请求体的,所以直接发送
xhr.send();
else if (method === 'POST' || method === 'PUT') {
//设置请求头--->设置请求体发送的数据的格式
xhr.setRequestHeader('Content-Type', 'application/json;charsrt=utf-8');
//将请求体内的对象转化为json格式的字符串后发送
xhr.send(JSON.stringify(data));
}
//4.判断是否成功
//绑定状态改变的监听事件
xhr.onreadystatechange = function () {
//解构函数 用readyState代替 xhr.readyState
const {readyState, status, statusText} = xhr;
//如果不是4直接return
if (readyState !== 4) return;
//如果成功执行resolve()
if (status >= 200 && status < 300) {
//成功的话会返回响应体,需要准备一个对象存储
const response = {
//响应的json对象转换为js
data: json.parse(xhr.response);
status,
statusText
}
resolve(response);
}
//如果失败执行reject()
else {
//返回一个error对象,并携带error.message
reject(new Error('request error status is :' + status));
}
}
})
}
- 使用封装的axios函数
js
//这边设置了四个button,分别绑定了点击事件GET、POST、PUT、DELETE
// GET请求 客户端获取数据
function GET() {
axios({
url: '请求的路径',
method: 'GET',
params: {
id: 2,
title: '甄嬛'
}
}).then(response => {
console.log(response);
},error => {
alert(error.message);
})
}
//POST请求 服务器保存数据
function POST() {
axios({
url: '请求的路径',
method: 'POST',
data: {
"title": "甄嬛",
"autor": "cloud"
}
}).then(response => {
console.log(response);
},error => {
alert(error.message);
})
}
//PUT请求 更新数据
function PUT () {
axios({
url: '请求的路径',
method: 'PUT',
data: {
"title": 'majo',
"autor": "goods"
}
}).then(response => {
console.log(response);
},error => {
alert(error.message);
})
}
//DELETE请求 更新数据
function PUT () {
axios({
url: '请求删除的路径',
method: 'DELETE'
}).then(response => {
console.log(response);
},error => {
alert(error.message);
})
}
axios
axios是一个基于promise、基于XHR进行二次封装的网络请求库。
特点
- 基于promise的异步ajax请求库。
- 浏览器/node都可以使用。
- 支持请求/响应拦截器 (统一对请求、响应做处理的函数)。
- 支持请求取消。
- 支持请求/响应数据转换。
- 支持批量发送多个请求。
常用API
js
axios(config) //可以设置任意类型请求的方式
axios(url[,config]) // 可以只指定url发get请求
axios.request(config) //等同于axios(config)
axios.get(url[,config]) //发get请求
axios.post(url[,config]) //发post请求
axios.put(url[,config]) //发put请求
axios.delelte(url[,config]) //发delete请求
axios.defaults.xxx //请求的 默认全局配置
axios.interceptors.request.use() //添加请求拦截器
axios.interceptors.response.use() //添加响应拦截器
axios.create([config]) //创建一个欣的axios
axios.Cancel() //用于创建取消请求的 错误对象
axios.CancelToken() //用于创建取消请求的 token对象
axios.isCancel() //是否是一个取消请求的错误
axios.all(promises) //用于批量执行多个异步请求
axios.spread() //与all配合使用,用来指定接收所有成功数据的回调函数 的方法
axios.create([config])
根据指定配置创建一个新的axios,也就是每个新的axios都有自己的位置,实现重新配置自身属性 ,向多个不同的服务器请求/发送数据。
-
新axios没有取消和批量发送请求的方法,其余都一样
为什么要设计这个语法?
-
需求:项目中有部分接口需要配置与另一部分接口不一样的内容,如何处理?
-
解决:创建2个新的axios,每个都有自己特有的配置,分别应用到不同需求的接口请求中。
-
js
//axios全局的baseURL
axios.defaults.baseURL = 'http://localhost:3000';
axios.get('/posts');
//当有多个服务器需要请求时
//可以通过创建一个新的axios去配置一些不同于全局,
//但是不止一个请求会使用的配置
//实现复用
const instance = axios.create({
//创建instance的baseURL
baseURL: 'http://localhost:4000'
})
//使用instance发送请求
instance.get('/posts'); //会向http://localhost:4000/posts发送请求
axios.interceptors拦截器
当多个请求接口有相同的配置、和相同的响应处理时,可以统一在拦截器里写,这样会减少代码的耦合度。
js
//创建一个请求
axios.get('http://localhost:3000/posts')
.then(response => {
console.log('response.data', response.data);
},
error => {
console.log('error:', error.message);
})
js
//创建2个请求拦截器和响应拦截器
//添加请求拦截器(回调函数)
axios.interceptors.request.use(
//成功的回调
config => {
console.log('request interceptor1 onResolved()');
return config
},
//失败的回调
error => {
console.log('request interceptor1 onRejected()');
return Promise.reject(error);
}
);
axios.interceptors.request.use(
config => {
console.log('request interceptor2 onResolved()');
return config
},
error => {
console.log('request interceptor2onRejected()');
return Promise.reject(error);
}
);
//添加响应拦截器
axios.interceptors.response.use(
response => {
console.log('response interceptor1 onResolved()');
return response
},
error => {
console.log('response interceptor1 onRejected()');
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
console.log('response interceptor2 onResolved()');
return response
},
error => {
console.log('response interceptor2 onRejected()');
return Promise.reject(error);
}
)
js
//执行顺序
//请求拦截器先进后出,响应拦截器按顺序执行,最后执行请求函数的回调
//源码时unshift压入所以先进后出
request interceptor2 onResolved()
request interceptor1 onResolved()
response interceptor1 onResolved()
response interceptor2 onResolved()
response.data (4) [{...}, {...}, {...}, {...}]
axios取消请求
想要实现取消必须添加配置cancelTok
js
//保存取消请求的函数
let cancel;
...
cancelToken: new axios.CancelToken((c) => { //c是用于取消当前请求的函数
//保存取消函数, 用于之后可能需要取消当前请求
cancel = c;
})
...
//执行取消请求
cancel();
实现
js
//保存取消请求的函数
let cancel;
function getProducts1 () {
axios.get('请求路径',{
cancelToken: new axios.CancelToken((c) => {
cancel = c;
})
}).then(response => {
cancel = null;// 请求成功了后无法取消
console.log(response.data);
},error => {
cancel = null;// 请求成已经失败了也无法取消
console.log('请求失败了',error.message);
})
}
function cancelRequest(cancel) {
//如果取消请求的函数存在则执行
//因为如果没有发起请求,则cancel没有内容,点击取消按钮是无效的
if (typeof cancel === 'function') {
//执行取消请求的函数
cancel('强制取消请求');
}else {
console.log('没有可取消的请求');
}
}
// 点击请求,请求中再点击取消请求,会打印 '请求失败了 没有可取消的请求'
// 当取消请求后,axios请求会走error,error对象也会变成Cancel特殊对象
//如果没有取消请求而是报错,则error就是Error对象
取消一个重复请求
只要在发请求之前判断cancel
是否为一个function
即可,如果是一个function,则说明有请求正在发送。
js
...
function getProducts1() {
//在发请求之前检查cancel是否是一个function
//如果是则说明有请求正在发送
if (typeof cancel === 'function') {
cancel('自动取消了上一个请求');
}
axios.get(...)...
...
问题
连续点击三次,只有第一次的会被取消 ,因为第二次点击,cancel=null
,在请求中的情况下,再一次点击请求按钮的时候,cancel依旧是null,就不会执行 if (typeof cancel === 'function') { cancel('取消了上一个请求'); }
所以就无法取消第二次请求。
所以需要加判断,如果是手动取消的请求不需要cancel = null
,这样每一次点击都可以通过if (typeof cancel === 'function') { cancel('取消了上一个请求'); }
去判断是否正在请求中。
js
...
error => {
if (axios.isCancel(error)) {
console.log('手动取消请求');
}
else {
cancel = null;
console.log('请求出错!');
}
}
耦合度
当不止一个请求,但素请求的某些配置又一样的时候,如果每请求一个接口都要这么写会很难看。 所以可以使用拦截器!!!
js
//请求拦截器
axios.interceptors.request.use(config => {
//在发请求之前检查cancel是否是一个function
//取消未完成的请求
if (typeof cancel === 'function') {
cancel('自动取消了上一个请求')
}
//添加一个cancelToken配置
config.cancelToken = new axios.CancelToken(c => {
cancel = c;
})
return config
})
//响应拦截器
axios.interceptors.response.use(response => {
cancel = null;
return response;
},error => {
if (axios.isCancel(error)) {
console.log('自动取消了上一个请求');
//在响应器里做出取消请求的处理结果,不需要请求里面的error做出处理
//又因为是链式调用,所以得在这里return一个空的promise结束链式调用,不往下传递
return new Promise(() => {})
}
else {
//在这里做取消请求的标记
cancel = null;
//但是处理结果还是在请求的error里处理
//因为每一个请求的接口的处理方式可能不同
// 所以要传递一个promise下去链式调用处理
return Promise.reject(error);
}
})
//这样下面的代码就会非常清爽
function getProducts1() {
axios.get('http://localhost:4000/products1').then(response => {
console.log(response.data);
},
error => {
console.log('请求出错1!', error.message);
})
}
function getProducts2() {
axios.get('http://localhost:4000/products2').then(response => {
console.log(response.data);
},
error => {
console.log('请求出错2!', error.message);
})
}
function cancelRequest(cancel) {
if (typeof cancel === 'function') {
cancel();
}
else {
console.log('抱歉哦亲,没有可取消的请求');
}
}
Ajax Fetch Axios
网络请求 | 特点 |
---|---|
Ajax | 一种思想、技术统称,主要利用XHR实现网络请求。 |
Fetch | 具体的一个API,基于promise,实现网络请求。 |
Axios | 一个封装库,基于XHR二次封装,也使用了promise,较为推荐使用。 |