Axios 笔记
Axios的使用和源码分析
·
官方文档地址:http://www.axios-js.com/zh-cn/docs/
Axios的理解和使用
1. json-server服务搭建
- 下载包:
npm i json-server -g
- 创建
db.json
文件 - 启动服务
json-server --watch db.json
db.json
{
"post": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}
可以请求的URL
http://localhost:3000/post
http://localhost:3000/comments
http://localhost:3000/profile
带参数的请求参考:https://github.com/typicode/json-server
2. axios的特点
- 基于 xhr + promise 的异步 ajax 请求库
- 浏览器端/node 端都可以使用
- 支持请求/响应拦截器
- 支持请求取消
- 请求/响应数据转换
- 批量发送多个请求
3. axios的基本使用
get请求
// axios(config)
axios({
method: "GET",
url: "http://localhost:3000/posts/2",
}).then((response) => {
console.log(response);
});
post请求
// axios(config)
axios({
method: "POST",
url: "http://localhost:3000/posts",
//请求体
data: {
title: "火焰戎装",
author: "水千丞",
},
}).then((response) => {
console.log(response);
});
// axios.post(url[, data, config])
axios.post({"http://localhost:3000/posts", {
title: "火焰戎装",
author: "水千丞",
}).then((response) => {
console.log(response);
});
put请求
// axios(config)
axios({
method: "PUT",
url: "http://localhost:3000/posts/3",
//请求体
data: {
title: "谁把谁当真",
author: "水千丞",
},
}).then((response) => {
console.log(response);
});
delete请求
// axios(config)
axios({
method: "DELETE",
url: "http://localhost:3000/posts/3",
}).then((response) => {
console.log(response);
});
4. axios 常用语法
- axios(config): 通用/最本质的发任意类型请求的方式
- axios.request(config): 等同于 axios(config)
- axios(url[, config]): 可以只指定 url 发 get 请求
- axios.get(url[, config]): 发 get 请求
- axios.delete(url[, config]): 发 delete 请求
- axios.post(url[, data, config]): 发 post 请求
- axios.put(url[, data, config]): 发 put 请求
5. 响应结果结构
- config:请求配置对象
- data:响应体的内容对象
- headers:响应头对象
- request:原生的AJAX请求对象(XMLHttpRequest实例对象)
- status:响应状态码
- statusText:响应状态字符串
6. axios的默认配置
所有的配置项都可以设置默认配置
axios.defaults.method = "GET";
axios.defaults.baseURL = "http://localhost:3000";
axios.defaults.timeout = 3000;
在后续使用中如果传入的配置项没有配置,就会使用默认配置
7. axios创建实例对象发送请求
const comments = axios.create({
baseURL: "http://localhost:3000",
timeout: 3000,
});
comments({ url: "/comments", method: "GET" }).then((response) => {
console.log(response);
});
// 也可以使用get方法指定请求方式
comments.get("/comments").then((response) => {
console.log(response);
});
这种方式类似于配置多组默认配置,可以减少一些重复配置项的书写
8. axios拦截器
配置拦截器
axios.interceptors.request.use(
(config) => {
console.log("请求拦截器...成功1");
return config;
// throw "参数出了点问题";
},
(error) => {
console.log("请求拦截器...失败1");
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log("响应拦截器...成功1");
return response.data;
},
(error) => {
console.log("响应拦截器...失败1");
return Promise.reject(error);
}
);
发送请求
axios("http://localhost:3000/comments")
.then((response) => {
console.log("自定义成功回调");
})
.catch((reason) => {
console.log("自定义失败回调");
});
配置完成的输出结果是:
也可以配置多个拦截器,需要注意的是:后配置的请求拦截器回调先执行,响应拦截器按照配置的顺序执行
可以在拦截器的回调中对请求的配置型和响应的数据进行处理
axios.interceptors.request.use(
(config) => {
console.log("请求拦截器...成功1");
config.params = { a: 100 };
return config;
},
(error) => {
console.log("请求拦截器...失败1");
return Promise.reject(error);
}
);
axios.interceptors.request.use(
(config) => {
console.log("请求拦截器...成功2");
config.timeout = 3000;
return config;
},
(error) => {
console.log("请求拦截器...失败2");
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log("响应拦截器...成功1");
return response.data;
},
(error) => {
console.log("响应拦截器...失败1");
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log("响应拦截器...成功2");
return response[0].id;
},
(error) => {
console.log("响应拦截器...失败2");
return Promise.reject(error);
}
);
响应拦截器回调按顺序,上一个回调的返回值是下一个回调收到的参数
问题:在每次进入回调前打印收到的参数,对于请求拦截器,在回调2调用时回调1还没有调用,但此时可以观察到回调1对于配置对象的修改
9. 取消请求
let cancel = null;
$btns.eq(0).on("click", () => {
// 如果上一次的请求还没有结束,直接取消重新进行请求
if (cancel) {
cancel();
}
axios({
method: "GET",
url: "http://localhost:3000/comments",
cancelToken: new axios.CancelToken((c) => {
cancel = c;
}),
}).then((response) => {
console.log(response);
cancel = null;
});
});
$btns.eq(1).on("click", () => {
cancel();
});
源码解析
1. 模拟axios对象的创建过程
function Axios(config) {
// 默认配置
this.defaults = config;
// 拦截器
this.intercepters = {
request: {},
response: {},
};
}
// 原型上要添加相关的方法
Axios.prototype.request = function (config) {
console.log("发送 Ajax 请求,请求的类型为:" + config.method);
};
Axios.prototype.get = function (config) {
return this.request({ method: "GET" });
};
Axios.prototype.post = function (config) {
return this.request({ method: "POST" });
};
//
function createInstance(config) {
// 实例化一个对象:此时可以作为对象使用,但是不能当做函数使用
let context = new Axios(config);
// 创建请求函数:此时是一个函数,不能够作为对象使用
let instance = Axios.prototype.request.bind(context);
// 为了解决问题:就要将 Axios.prototype 中的方法添加到 instance 中
Object.keys(Axios.prototype).forEach((key) => {
instance[key] = Axios.prototype[key].bind(context);
});
// 还要将原本的axios对象上的defaults和intercepters也添加上
Object.keys(context).forEach((key) => {
instance[key] = context[key];
});
return instance;
}
let axios = createInstance();
// 此时可以作为函数发送请求,也可以作为对象发送请求
axios.get();
axios.post();
axios({ method: "Get" });
2. 模拟实现 Axios 发送请求
function Axios(config) {
this.config = config;
}
Axios.prototype.request = function (config) {
// 创建一个promise对象
let promise = Promise.resolve(config);
// 声明一个数组
let chains = [dispatchRequest, undefined];
// 循环处理数组
let result = promise.then(chains[0], chains[1]);
// 返回promise结果
return result;
};
function dispatchRequest(config) {
// 调用适配器发送请求
return xhrAdapter(config).then(
(response) => {
// 处理请求的结果
return response;
},
(error) => {
throw error;
}
);
}
function xhrAdapter(config) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open(config.method, config.url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status <= 300) {
resolve({
config: config,
data: xhr.response,
headers: xhr.getAllResponseHeaders(),
request: xhr,
status: xhr.status,
statusText: xhr.statusText,
});
} else {
reject(new Error("请求失败,失败的状态码为" + xhr.status));
}
}
};
});
}
let axios = Axios.prototype.request.bind(null);
axios({
method: "GET",
url: "https://autumnfish.cn/search?keywords=你",
}).then((value) => {
console.log(value);
});
3. 模拟实现 Axios 拦截器功能
function Axios(config) {
this.config = config;
this.interceptors = {
request: new InterceptorsManager(),
response: new InterceptorsManager(),
};
}
// 拦截器管理器构造函数
function InterceptorsManager() {
this.handlers = [];
}
InterceptorsManager.prototype.use = function (fullfilled, rejected) {
this.handlers.push({ fullfilled, rejected });
};
Axios.prototype.request = function (config) {
let promise = Promise.resolve(config);
const chains = [dispatchRequest, undefined];
// 将请求拦截器的回调压入到 chains 的前面
this.interceptors.request.handlers.forEach((value) => {
chains.unshift(value.fullfilled, value.rejected);
});
this.interceptors.response.handlers.forEach((value) => {
chains.push(value.fullfilled, value.rejected);
});
// 遍历调用:请求拦截器 请求 相应拦截器
while (chains.length > 0) {
promise = promise.then(chains.shift(), chains.shift());
}
return promise;
};
function dispatchRequest() {
return new Promise((resolve, reject) => {
resolve({
status: 200,
statusText: "OK",
});
});
}
let context = new Axios({});
let axios = Axios.prototype.request.bind(context);
Object.keys(context).forEach((key) => {
axios[key] = context[key];
});
axios.interceptors.request.use(
(config) => {
console.log("请求拦截器...成功1");
config.params = { a: 100 };
return config;
},
(error) => {
console.log("请求拦截器...失败1");
return Promise.reject(error);
}
);
axios.interceptors.request.use(
(config) => {
console.log("请求拦截器...成功2");
config.timeout = 3000;
return config;
},
(error) => {
console.log("请求拦截器...失败2");
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log("响应拦截器...成功1");
return response;
},
(error) => {
console.log("响应拦截器...失败1");
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log("响应拦截器...成功2");
return response;
},
(error) => {
console.log("响应拦截器...失败2");
return Promise.reject(error);
}
);
axios({}).then((value) => {
console.log(value);
});
4. 模拟实现 Axios 取消请求功能
function Axios(config) {
this.config = config;
}
// 原型request方法
Axios.prototype.request = function (config) {
return dispatchRequest(config);
};
// dispatchRequest函数
function dispatchRequest(config) {
return xhrAdapter(config);
}
// xhrAdapter
function xhrAdapter(config) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open(config.method, config.url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status <= 300) {
resolve({
status: xhr.status,
statusText: xhr.statusText,
});
} else {
reject(new Error("请求失败,失败的状态码为" + xhr.status));
}
}
};
if (config.cancelToken) {
config.cancelToken.promise.then((value) => {
xhr.abort();
console.log("请求取消执行");
});
}
});
}
// 创建 axios 函数
let context = new Axios({});
let axios = Axios.prototype.request.bind(context);
// CancelToken 构造函数
function CancelToken(executor) {
// 声明一个变量
var resolvePromise;
// 为实例对象添加属性
this.promise = new Promise((resolve) => {
resolvePromise = resolve;
});
// 调用 executor 函数
executor(function () {
// 执行 resolvePromise 函数,改变 promise 的状态
resolvePromise();
});
}
let cancel = null;
document.getElementById("send").onclick = function () {
// 如果上一次的请求还没有结束,直接取消重新进行请求
if (cancel) {
cancel();
}
axios({
method: "GET",
url: "https://autumnfish.cn/search?keywords=我",
cancelToken: new CancelToken(function (c) {
cancel = c;
}),
}).then(
(response) => {
console.log(response);
cancel = null;
},
(error) => {
console.log(error);
}
);
};
document.getElementById("cancel").onclick = function () {
cancel();
};
总结
axios 与 Axios 的关系
- 从语法上来说:axios 不是 Axios 的实例
- 从功能上来说:axios 是 Axios 的实例
- axios 是 Axios.prototype.request 函数 bind() 返回的函数
- axios 作为对象有 Axios 原型对象上的所有方法,有 Axios 对象上所有属性
instance 与 axios 的区别
- 都是一个能发送任意请求的函数:request(config)
- 都有发送特定请求的各种方法:get()/post()/put()/delete()
- 都有默认配置和拦截器的属性:defaults/interceptors
- 默认配置很可能不一样
- instance 没有axios 后面添加的一些方法:create()/CancelToken()/all()
错误结果结构:
- message
- request
- response
更多推荐
已为社区贡献1条内容
所有评论(0)