axios-引入-常用语法-源码
axios1 axios的引入1.1. axios是什么?前端最流行的ajax请求库react/vue官方都推荐使用axios发ajax请求文档: https://github.com/axios/axios1.2.axios特点基于xhr + promise的异步ajax请求库浏览器端/node端都可以使用支持请求/响应拦截器支持请求取消请求/响应数据转换批量发送多个请求1.3axios引入引入
axios
1 axios的引入
1.1. axios是什么?
-
前端最流行的ajax请求库
-
react/vue官方都推荐使用axios发ajax请求
-
文档: https://github.com/axios/axios
1.2. axios特点
- 基于xhr + promise的异步ajax请求库
- 浏览器端/node端都可以使用
- 支持请求/响应拦截器
- 支持请求取消
- 请求/响应数据转换
- 批量发送多个请求
1.3 axios引入
引入:npm init -y
安装:npm install axios
使用中引入axios
方法一:
- 安装好以后回生成node_modules文件夹,在当前目录下直接引入./node_modules/axios/dist/axios.min.js
方法二:
- 在猫云下搜索axios,复制链接引入
2 axios的基本使用
创建站点服务data.json,打开小黑屏
json-server data.json -w -p 8090 -H 127.0.0.1
共同点:都需要传参 都是有请求地址 返回数据
2.1 get获取数据
使用params 将对象转化为字符串,和地址进行拼接
btns[0].onclick = async function() {
// const result = axios.get("http://127.0.0.1/scoreList",{
// params:{
// sex:"男"
// }
// })
// // result.then(value=>{
// // console.log(value.data)
// // })
// 或者用解构赋值
// result.then(({data})=> {
// console.log(data)
// })
// 使用await
const {data} = await axios.get("http://127.0.0.1/scoreList", {
params:{
age:12
}
})
console.log(data)
}
2.2 post添加数据
闪退更新的问题,新建server,创建express
const express = require("express");
const app = express();
app.use(eapress.static(__dirname))
app.listen(80,function() {
console.log("success")
})
**注意:**服务端访问的地址要为localhost下默认地址
当第二个参数是对象时:content-type,application/json 传递的参数是对象
btns[1].onclick = async function() {
const result = await axios.post("http://localhost:3000/scoreList", {
userName:"zhangsan"
})
console.log(result.data)
}
当第二个参数是字符串时:content-type,application/x-www-form-urlencoded 传递的参数是字符串
btns[1].onclick = async function() {
const result = await axios.post("http://localhost:3000/scoreList","age=100")
console.log(result.data)
}
2.3 put更新数据(完整)
btns[2].onclick = async function() {
const result = await axios.put("http://127.0.0.1:3000/scoreList/8",{
userName:"laodai"
})
console.log(result.data)
}
2.4 patch 更新数据(局部)
btns[3].onclick = async function() {
const result = await axios.patch("http://127.0.0.1:3000/scoreList/7",{
userName:"laoqian"
})
console.log(result.data)
}
2.5 delete删除数据
btns[4].onclick = async function() {
const result = await axios.delete("http://127.0.0.1:3000/scoreList/11",{
userName:"zhangsan"
})
console.log(result.data)
}
2.6 head获取数据(get)
head有priview和response内容的显示
head得不到响应的内容,用于验证请求资源是否存在
2.7 axios 是一个函数
不写method 等于axios.get()
写method 等于axios.post()
测试timeout,json-server --watch data.json --delay 3000 延迟三秒反应
axios({
method:"post",
url:"http://127.0.0.1:3000/scoreList",
data:"a=1&b=2",//放置到请求体当中
params:{
c:3
},//转化为字符串和地址进行拼接
headers:{
authorization:"abxfde"
},
timeout:1000
}).then(value=>{
console.log(value)
}).catch(err=>{
console.log(111,err)
})
2.8 通过xhr模拟axios(了解)
axios是一个函数 模拟axios() axios.get() axios.post()
function axios(options={}){
let {method="get",data="",url="",params={}} = options;
return new Promise(function (resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.responseType="json";
if(options.timeout)
xhr.timeout = options.timeout;// 增加超时设置
// {c:3,d:4} http://localhost:3000/scoreList
// http://localhost:3000/scoreList?c=3&d=4
// [c=3,d=4]
url=url+(url.includes("?")?"&":"?")+Object.keys(params).map(v=>v+"="+params[v]).join("&")
xhr.open(method,url);
if(method.toLowerCase() === "post"){
if(typeof data === "object"){
xhr.setRequestHeader("content-type","application/json");
xhr.send(JSON.stringify(data));
}
else{
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
xhr.send(data);
}
}else{
xhr.send();
}
xhr.ontimeout = function(){
reject("请求超时");
}
xhr.onload = function () {
resolve({
// 配置对象
config:options,
// 响应体的内容,格式 json
data:xhr.response,
// 响应头的内容
headers:xhr.getAllResponseHeaders(),
// 发起请求的xhr实例
request:xhr,
// 得到的状态码
status:xhr.status,
// 得到的状态码说明
statusText:xhr.statusText
});
}
})
}
axios.get = function (url,config) {
return axios({
method:"get",
url,
params:config.params
})
}
axios.post = function (url,data) {
return axios({
method:"post",
url,
data
})
}
2.9 axios实例
当项目当中请求接口在不同的服务器时,可以通过create创建axios实例
const a1 = axios.create({
baseURL:"http://localhost:3000",
timeout:1000,
})
const a2 = axios.create({
baseURL:"http://127.0.0.1:3000",
})
a1({
method:"post",
url:"/scoreList"
})
a2.get("/scoreList")
3 axios常用语法
3.1 数据的请求与响应的转换
transformRequest:请求时发送的数据,该函数返回的值,才是真正发送的数据,,返回的内容其实是传递给了xhr.send() 将请求的数据进行二次设置
axios ({
method:"post",
url:"http://127.0.0.1:3000/scoreList",
data:{
a:1,
b:2
},
trandformRequest(data,headers) {//post put patch 中实现
data = {
...data,
c:100
}
headers["Content-Type"]="application/json"
return JSON.stringify(data) //相当于send当中的内容
}
}).then(value=>{
console.log(value)
})
transformResponse:处理响应的数据,res是响应体的内容,返回的结果,才是真正得到的数据
// 处理响应的数据,res是响应体的内容。
transformResponse(res){
// 返回的结果 ,即是真正得到的数据(data)
// const {ok,msg} = JSON.parse(res);
// if(ok === -1){
// alert(msg);
// }
return JSON.parse(res);
}
3.2 默认配置
设置默认项
axios.defaults.baseURL = "http://localhost:3000";
axios.defaults.headers["abcdefg"] = "11111";
axios.defaults.timeout= 1000;
axios.get("/scoreList")
3.3 拦截器(重要)
① 请求拦截
axios.interceptors.request.use():发送请求之前执行, 拦截函数会被执行,接收的是axios配置对象
// 请求拦截:发送请求之前执行
axios.interceptors.request.use(function(config) {
// 拦截函数会被执行,接收的是axio配置对象(config)
config.params.sex = "男"//拦截的时候可以进行更改 没有return救护报错
return config;//拦下什么也没干
})
注意:请求拦截接定义多次,先定义的后执行
② 响应拦截
axios.interceptors.response.use() axios得到响应后,会执行响应拦截。返回的内容就是响应之后得到的数据
axios.interceptors.response.use(function(res) {
console.log(res) //res的响应内容
return res.data;//返回的内容就是响应之后的数据 没有return得到的就是undefined
})
注意:响应拦截如果写多个,先定义的先执行
请求拦截和响应拦截也具有异常穿透的功能,因为它是由若干个promise组成的
axios是基于promise通过xhr发送请求
axios.interceptors.request.eject() :取消请求拦截 (通过下标来进行取消)
axios.interceptors.response.eject() :取消响应拦截
3.4 all
用于批量执行多个异步请求
加上拦截器,输出的是一个数组
axios.interceptors.response.use(function(res) {
axios.get("http://127.0.0.1:3000/scoreList",{
params:{
sex:"男"
}
}).then(data=>{
console.log(data)
})
axios.get("http://127.0.0.1:3000/scoreList",{
params:{
sex:"男"
}
}).then(data=>{
console.log(data)
})
})
// 将两者合并
const a1 = axios.get("http://127.0.0.1:3000/scoreList",{params:{sex:男}})
const a1 = axios.get("http://127.0.0.1:3000/scoreList",{params:{sex:女}})
axios.all([a1,a2]).then(value=>{
console.log(value)
})
all原理
function myAxios(){
}
myAxios.all = function (aArr) {
return Promise.all(aArr);
}
myAxios.all([a1,a2]).then(value=>{
console.log(value);
})
3.5 spread
用来指定接收所有成功数据的回调函数的方法
const a1 = axios.get("http://localhost:3000/scoreList",{params:{sex:"男"}});
const a2 = axios.get("http://localhost:3000/scoreList",{params:{sex:"女"}});
axios.all([a1,a2]).then(axios.spread(function (one,two) {
console.log(one,two);
}));
spread原理
function spread(cb){
return function (value) {// 成功
// console.log("成功回调",value)
// cb(value[0],value[1])
// cb(...value);
cb.apply(null,value);// cb.apply(null,[1,2])
}
}
3.6 取消请求
axios.Cancel():用于创建取消请求的错误对象
axios.CancelToken() : 用于创建取消请求的token对象
axios.isCancel(): 是否是一个取消请求的错误
-
基本流程
配置cancelToken对象
缓存用于取消请求的cancel函数
在后面特定时机调用cancel函数取消请求
在错误回调中判断如果error是cancel, 做相应处理
-
实现功能
点击按钮, 取消某个正在请求中的请求
在请求一个接口前, 取消前面一个未完成的请求
const btns = document.querySelectorAll("button")
let cancelFn = null;
btns[0].onclick = function () {
if (cancelFn instanceof Function) {
cancelFn();
}
axios.get("http://127.0.0.1:3000/scoreList",{
params:{
sex:"男"
},
cancelToken:new axios.CancelToken(function(cb) {
cancelFn = cb
}),
timeout:3000//json-server --watch data.json --delay 3000 延迟三秒反应
}).then(value=>{
console.log(value)
}).catch(err=>{
console.log(axios.isCancel(err))// 判断错误信息是否为取消请求造成的。true
})
}
btns[1].onclick = function () {
cancelFn("取消请求")
}
取消原理
const btns = document.querySelectorAll("button");
let cancelFn = null;
function axios(config){
return new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest();
xhr.open("get",config.url);
xhr.send();
xhr.onload = function () {
resolve(JSON.parse(xhr.responseText));
}
xhr.onabort = function () {
reject("取消请求")
}
if(config.cancelToken){
config.cancelToken.cancel.then(value=>{
// console.log("执行了")
xhr.abort();// 取消请求
})
}
})
}
axios.CancelToken = function(callback){
this.cancel = new Promise(resolve=>{
callback(resolve)
})
}
btns[1].onclick = function(){
cancelFn()
}
btns[0].onclick = function () {
axios({
url:"http://localhost:3000/scoreList",
cancelToken:new axios.CancelToken(function (cb) {
cancelFn=cb;
})
}).then(value=>{
console.log(value);
}).catch(err=>{
console.log("异常",err);
})
}
3 axios的理解
3.1. axios常用语法总结
axios(config): 通用/最本质的发任意类型请求的方式
axios(url[, config]): 可以只指定url发get请求
axios.request(config): 等同于axios(config)
axios.get(url[, config]): 发get请求
axios.delete(url[, config]): 发delete请求
axios.post(url[, data, config]): 发post请求
axios.put(url[, data, config]): 发put请求
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(): 用来指定接收所有成功数据的回调函数的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gsaDLL5B-1621944122791)(file:///C:\Users\王秀\AppData\Local\Temp\ksohtml3944\wps4.png)]
工厂对象:调用方提供或者有默认值,直接调用
creat是axios下的属性和方法,两者都是函数
3.2. 难点语法的理解和使用
3.2.1. axios.create(config)
-
根据指定配置创建一个新的axios, 也就就每个新axios都有自己的配置
-
新axios只是没有取消请求和批量发请求的方法, 其它所有语法都是一致的
-
为什么要设计这个语法?
(1) 需求: 项目中有部分接口需要的配置与另一部分接口需要的配置不太一样, 如何处理
(2) 解决: 创建2个新axios, 每个都有自己特有的配置, 分别应用到不同要求的接口请求中
3.2.2 拦截器函数/ajax请求/请求的回调函数的调用顺序
-
说明: 调用axios()并不是立即发送ajax请求, 而是需要经历一个较长的流程
-
流程: 请求拦截器2 => 请求拦截器1 => 发ajax请求 => 响应拦截器1 => 响应拦截器2 => 请求的回调
-
注意: 此流程是通过promise串连起来的, 请求拦截器传递的是config, 响应拦截器传递的是response
-
请求以及响应拦截是可以写多个的,请求拦截先定义后执行,响应拦截先定义先执行
4 axios源码分析
4.1. 源码目录结构
├── /dist/ # 项目输出目录
├── /lib/ # 项目源码目录
│ ├── /adapters/ # 定义请求的适配器 xhr、http
│ │ ├── http.js # 实现http适配器(包装http包)
│ │ └── xhr.js # 实现xhr适配器(包装xhr对象)
│ ├── /cancel/ # 定义取消功能
│ ├── /core/ # 一些核心功能
│ │ ├── Axios.js # axios的核心主类
│ │ ├── dispatchRequest.js # 用来调用http请求适配器方法发送请求的函数
│ │ ├── InterceptorManager.js # 拦截器的管理器
│ │ └── settle.js # 根据http响应状态,改变Promise的状态
│ ├── /helpers/ # 一些辅助方法
│ ├── axios.js # 对外暴露接口
│ ├── defaults.js # axios的默认配置
│ └── utils.js # 公用工具
├── package.json # 项目信息
├── index.d.ts # 配置TypeScript的声明文件
└── index.js # 入口文件
4.2 源码分析
4.2.1. axios与Axios的关系?
-
从语法上来说: axios不是Axios的实例
-
从功能上来说: axios是Axios的实例
-
axios是Axios.prototype.request函数bind()返回的函数
-
axios作为对象有Axios原型对象上的所有方法, 有Axios对象上所有属性
4.2.2. instance与axios的区别?
- 相同:
(1) 都是一个能发任意请求的函数: request(config)
(2) 都有发特定请求的各种方法: get()/post()/put()/delete()
(3) 都有默认配置和拦截器的属性: defaults/interceptors
- 不同:
(1) 默认配置很可能不一样
(2) instance没有axios后面添加的一些方法: create()/CancelToken()/all()
4.2.3. axios运行的整体流程?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWeVGs13-1621944122795)(file:///C:\Users\王秀\AppData\Local\Temp\ksohtml3944\wps6.png)]
- 整体流程:
request(config) ==> dispatchRequest(config) ==> xhrAdapter(config)
- request(config):
将请求拦截器 / dispatchRequest() / 响应拦截器 通过promise链串连起来, 返回promise
- dispatchRequest(config):
转换请求数据 ===> 调用xhrAdapter()发请求 ===> 请求返回后转换响应数据. 返回promise
- xhrAdapter(config):
创建XHR对象, 根据config进行相应设置, 发送特定请求, 并接收响应数据, 返回promise
4.2.4. axios的请求/响应拦截器是什么?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O7K4tOi0-1621944122798)(file:///C:\Users\王秀\AppData\Local\Temp\ksohtml3944\wps7.png)]
- 请求拦截器:
在真正发送请求前执行的回调函数
可以对请求进行检查或配置进行特定处理
成功的回调函数, 传递的默认是config(也必须是)
失败的回调函数, 传递的默认是error
- 响应拦截器
在请求得到响应后执行的回调函数
可以对响应数据进行特定处理
成功的回调函数, 传递的默认是response
失败的回调函数, 传递的默认是error
4.2.5. axios的请求/响应数据转换器是什么?
- 请求转换器: 对请求头和请求体数据进行特定处理的函数
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, ‘application/json;charset=utf-8’);
return JSON.stringify(data);
}
- 响应转换器: 将响应体json字符串解析为js对象或数组的函数
response.data = JSON.parse(response.data)
4.2.6. response的整体结构
{
data,
status,
statusText,
headers,
config,
request
}
4.2.7. error的整体结构
{
message,
response,
request,
}
4.2.8. 如何取消未完成的请求?
- 当配置了cancelToken对象时, 保存cancel函数
(1) 创建一个用于将来中断请求的cancelPromise
(2) 并定义了一个用于取消请求的cancel函数
(3) 将cancel函数传递出来
- 调用cancel()取消请求
(1) 执行cacel函数, 传入错误信息message
(2) 内部会让cancelPromise变为成功, 且成功的值为一个Cancel对象
(3) 在cancelPromise的成功回调中中断请求, 并让发请求的proimse失败, 失败的reason为Cancel对象
源码分析代码:
function Axios(config){
}
Axios.prototype.request = function () {
}
Axios.prototype.get = function () {
}
Axios.prototype.post = function () {
}
function createInstance(defaultConfig) {
// 实例化Axios,将默认参数(defaultConfig)进行传递
var context = new Axios(defaultConfig);
//将content(Axios的实例)当中的属性以及方法放置到request当中
var instance = Axios.prototype.request.bind(context);
// Copy axios.prototype to instance
// instance:request函数(this指向到axios实例) ,原型对象,axios实例
// 直白:将Axios原型对象当中的属性和方法,复制到了request函数对象中。
Object.keys(Axios.prototype).forEach(key=>{
if(Axios.prototype[key] instanceof Function){
instance[key] = Axios.prototype[key].bind(context);
}else{
instance[key]= Axios.prototype[key];
}
})
Object.keys(context).forEach(key=>{
if(context[key] instanceof Function){
instance[key]= context[key].bind(context);
}else{
instance[key] = context[key];
}
})
return instance;
}
const axios = createInstance({});
4.3. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UidG0Yg0-1621944122800)(file:///C:\Users\王秀\AppData\Local\Temp\ksohtml3944\wps8.png)]Axios二次封装
4.3.1. 功能点
-
统一进行请求配置: 基础路径/超时时间等
-
请求过程中loading提示
-
请求可能需要携带token数据
-
请求成功的value不再是response, 而是response.data
-
请求失败/出错统一进行处理, 每个请求可以不用单独处理
4.3.2. 编码实现与测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Axios二次封装</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/nprogress/0.2.0/nprogress.css">
</head>
<body>
<div>
<button onclick="getUsers()">获取用户列表</button>
<button onclick="getRepos()">获取仓库列表</button>
</div>
<!--
测试接口1: https://api.github.com/search/repositories?q=v&sort=stars
测试接口1: https://api.github.com/search/users?q=v
-->
<!--
1). 统一进行请求配置: 基础路径/超时时间等
2). 请求过程中loading提示
3). 请求可能需要携带token数据
4). 请求成功的value不再是response, 而是response.data
5). 请求失败/出错统一进行处理, 每个请求可以不用单独处理
-->
<script src="https://cdn.bootcss.com/axios/0.19.0/axios.js"></script>
<script src="https://cdn.bootcss.com/nprogress/0.2.0/nprogress.js"></script>
<script>
const instance = axios.create({
baseURL: 'https://api.github.com',
timeout: 15000
})
instance.interceptors.request.use(config => {
NProgress.start()
// 从local中读取前面保存的token数据
const token = localStorage.getItem('token_key') || 'xxxxx'
if (token) {
config.headers['token'] = token // 请求头的名称为后台接口指定
}
return config
})
instance.interceptors.response.use(
response => {
NProgress.done()
return response.data
},
error => {
NProgress.done()
alert('请求出错了', error)
return error
}
)
</script>
<script>
function getUsers () {
instance.get('/search/users', {
params: {
q: 'v'
}
}).then(result => {
console.table(result.items)
})
}
function getRepos () {
instance.get('/search/repositories', {
params: {
q: 'v',
sort: 'stars'
}
}).then(result => {
console.table(result.items)
})
}
</script>
</body>
</html>
const token = localStorage.getItem('token_key') || 'xxxxx'
if (token) {
config.headers['token'] = token // 请求头的名称为后台接口指定
}
return config
})
instance.interceptors.response.use(
response => {
NProgress.done()
return response.data
},
error => {
NProgress.done()
alert('请求出错了', error)
return error
}
)
```更多推荐
所有评论(0)