1.从基本使用去分析axios
// 方式一:
axios({
method: 'get',
url: 'http://localhost:5000/getTest'
})
// 方式二:
axios.get('http://localhost:5000/getTest')
结论:axios可以通过传进去一个配置对象去发送ajax,或者通过axios.xxx去发起ajax。
2.实现axios
实现axios({})这种形式:
class MyAxios{
request(config) {
return this.sendAjax(config)
}
sendAjax(config) {
return new Promise((resolve, reject) => {
// 原生的ajax
const {method = 'get', url= '', data= {}} = config
const xhr = new XMLHttpRequest()
xhr.onload = ()=>{
resolve(xhr.response)
}
xhr.open(method, url, true)
xhr.send(data)
})
}
}
// 创建axios的function
function createAxiosFn() {
const axios = new MyAxios()
// 将axios上的request方法赋值给req
const req = axios.request.bind(axios)
return req
}
const axios = createAxiosFn()
axios({
method: 'get',
url: 'http://localhost:5000/getTest',
}).then((res) => {
console.log(res); // 打印出来就成功了
});
实现axios.xxx方式:
class MyAxios{
request(config) {
return this.sendAjax(config)
}
sendAjax(config) {
return new Promise((resolve, reject) => {
const {method = 'get', url= '', data= {}} = config
const xhr = new XMLHttpRequest()
xhr.onload = ()=>{
resolve(xhr.response)
}
xhr.open(method, url, true)
xhr.send(data)
})
}
}
// 常用的http请求方法
const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options']
methods.forEach(mt => {
MyAxios.prototype[mt] = function() {
// 这四种http请求方法没有data
const _arr = ['get', 'head', 'options', 'delete']
if(_arr.includes(mt)) {
return this.request({
method: mt,
url: arguments[0],
...(arguments[1] || {})
})
} else {
return this.request({
method: mt,
url: arguments[0],
data: arguments[1],
...(arguments[2] || {})
})
}
}
})
// 通用的继承方法, target:目标对象, origin:复制对象 ctx:this
function extend(target, origin, ctx) {
Object.keys(origin).forEach(key => {
if(origin.hasOwnProperty(key)) {
if(typeof origin[key] === 'function') {
target[key] = origin[key].bind(ctx)
} else {
target[key] = origin[key]
}
}
})
}
function createAxiosFn() {
const axios = new MyAxios()
const req = axios.request.bind(axios)
// req继承MyAxios.prototype上的属性,this执行axios
extend(req, MyAxios.prototype, axios)
return req
}
const axios = createAxiosFn()
axios.get('http://localhost:5000/getTest').then((res) => {
console.log(res); // 打印则代表成功
});
3.实现axios的拦截器
// 拦截器的管理器
class InterceptorManage {
constructor() {
this.handlers = [];
}
use(fulfilled, rejected) {
// 存储拦截器的两个方法,用对象存储
this.handlers.push({
fulfilled,
rejected,
});
}
}
class MyAxios {
constructor() {
this.interceptor = {
request: new InterceptorManage(),
response: new InterceptorManage(),
};
}
request(config) {
const chains = [this.sendAjax.bind(this), undefined];
this.interceptor.request.handlers.forEach((hd) => {
chains.unshift(hd.fulfilled, hd.rejected);
});
this.interceptor.response.handlers.forEach((hd) => {
chains.push(hd.fulfilled, hd.rejected);
});
let promise = Promise.resolve(config);
while (chains.length > 0) {
promise = promise.then(chains.shift(), chains.shift());
}
return promise;
}
sendAjax(config) {
return new Promise((resolve, reject) => {
const { method = 'get', url = '', data = {} } = config;
const xhr = new XMLHttpRequest();
xhr.onload = () => {
resolve(xhr.response);
};
xhr.open(method, url, true);
xhr.send(data);
});
}
}
const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'];
methods.forEach((mt) => {
MyAxios.prototype[mt] = function () {
const _arr = ['get', 'head', 'options', 'delete'];
if (_arr.includes(mt)) {
return this.request({
method: mt,
url: arguments[0],
...(arguments[1] || {}),
});
} else {
return this.request({
method: mt,
url: arguments[0],
data: arguments[1],
...(arguments[2] || {}),
});
}
};
});
function extend(target, origin, ctx) {
Object.keys(origin).forEach((key) => {
if (origin.hasOwnProperty(key)) {
if (typeof origin[key] === 'function') {
target[key] = origin[key].bind(ctx);
} else {
target[key] = origin[key];
}
}
});
}
function createAxiosFn() {
const axios = new MyAxios();
const req = axios.request.bind(axios);
extend(req, MyAxios.prototype, axios);
extend(req, axios, axios);
return req;
}
const axios = createAxiosFn();
axios.interceptor.request.use(
(config) => {
console.log('请求拦截');
return config;
},
(err) => {
return err;
}
);
axios.interceptor.response.use(
(res) => {
console.log('response拦截');
return res;
},
(err) => {
return err;
}
);
axios.get('http://localhost:5000/getTest').then((res) => {
console.log(res);
});
拦截器的工作原理:
每一个拦截器就是一个promise,只不过只有发送ajax的那个需要耗费久一点的时间而已。我们每axios.interceptor.xxx.use
就会往chains这个数组插入一个拦截器。
// 初始化chains数组,让发送ajax的promise位于中间
const chains = [this.sendAjax.bind(this), undefined];
// request的拦截器就放在发送ajax的前面
this.interceptor.request.handlers.forEach((hd) => {
chains.unshift(hd.fulfilled, hd.rejected);
});
// response的拦截器就放在发送ajax的后面
this.interceptor.response.handlers.forEach((hd) => {
chains.push(hd.fulfilled, hd.rejected);
});
// 这里形成我们上面那幅图的结构
let promise = Promise.resolve(config); // 起点
while (chains.length > 0) {
promise = promise.then(chains.shift(), chains.shift()); // 中间的promise
}
return promise; // 能获取结果的promise
收获:看了源码,只能更好的使用和理解axios。axios的拦截器的实现思路真的很棒,利用promise的链子一条一条的传下去。