手写axios
导读
- 这里主要实现了
- 构建
axios
对象和函数 - 发送网络请求:
ajax
- 拦截器
- 适配器
CancolToken
功能
- 构建
axios
index.html
- 这里直接使用
esmodule
, 需要注意import
时候不能省略.js
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<body>
<button> 发送请求 </button>
<script type="module">
import axios from './index.js'
// fetch('./db.json').then(async (res) => console.log(await res.json()))
axios.interceptors.request.use(function one(config) {
console.log('请求拦截器 成功 - 1号');
return config;
}, function one(error) {
console.log('请求拦截器 失败 - 1号');
return Promise.reject(error);
});
axios.interceptors.request.use(function two(config) {
console.log('请求拦截器 成功 - 2号');
return config;
}, function two(error) {
console.log('请求拦截器 失败 - 2号');
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 1号');
return response;
}, function (error) {
console.log('响应拦截器 失败 1号')
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 2号')
return response;
}, function (error) {
console.log('响应拦截器 失败 2号')
return Promise.reject(error);
});
const btns = document.querySelectorAll('button')
let cancel = null;
btns[0].onclick = function () {
if (cancel !== null) {
cancel();
}
let cancelToken = new axios.CancelToken(
function (c) { cancel = c });
axios({
method: 'GET',
url: 'http://localhost:3000/posts',
// url: './db.json',
cancelToken: cancelToken
}).then(response => {
cancel = null;
console.log(response.data);
})
}
</script>
</body>
index.js
- 导出构造函数
1
2import axios from './Axios.js'
export default axios
Axios.js
- 构建了
Axios
的类,并创造了他的实例对象 - 此时就可以
axios.get
oraxios({method:"GET"})
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
36import { request } from './request.js'
import { CancelToken } from './CancelToken.js'
import { InterceptorManager } from './interceptor.js'
class Axios {
constructor(config) {
this.defaults = config;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
}
}
request(config) {
return request.call(this, config)
}
get(config) {
return this.request({ method: "GET" })
}
post() {
return this.request({ method: "POST" })
}
}
function createInstance(config) {
let context = new Axios(config);
let instance = Axios.prototype.request.bind(context);
let functionNamesArray = Object.getOwnPropertyNames(Axios.prototype)
functionNamesArray.forEach(item => {
instance[item] = Axios.prototype[item].bind(context)
})
Object.keys(context).forEach(key => {
instance[key] = context[key];
});
return instance
}
let axios = createInstance()
axios.CancelToken = CancelToken
export default axios
dispatchRequest.js
- 这里用于调用适配器
1
2
3
4
5
6
7
8import { xhrAdapter } from './xhrAdapter.js'
export function dispatchRequest(config) {
return xhrAdapter(config).then(response => {
return response;
}, error => {
throw error;
});
}
xhrAdapter.js
- 适配器:用于切换使用
node
版本还是js
版本 - 这里指写了
js
版本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
29export 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.responseText,
headers: xhr.getAllResponseHeaders(),
request: xhr,
status: xhr.status,
statusText: xhr.statusText
});
} else {
reject(new Error('请求失败 失败的状态码为' + xhr.status));
}
}
}
if (config.cancelToken) {
config.cancelToken.promise.then(value => {
xhr.abort();
reject(new Error('请求已经被取消'))
});
}
});
}
interceptor.js
- 拦截器的实例对象
1
2
3
4
5
6
7
8export class InterceptorManager {
constructor() {
this.handlers = [];
}
use(fulfilled, rejected) {
this.handlers.push({ fulfilled, rejected })
}
}
request.js
- 这里是关于拦截器的核心代码
- 可以看出是用一个数组维护 响应拦截器 和 请求拦截器
- 放的时候 请求拦截器
unshift
, 相应拦截器push
- 取的时候 请求拦截器
shift
, 相应拦截器shift
- 因此当多个拦截器执行的时候 请求拦截器的顺序是反的 相应拦截器的正的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import { dispatchRequest } from './dispatchRequest.js'
export function request(config) {
let promise = Promise.resolve(config);
let chains = [dispatchRequest, undefined];
this.interceptors.request.handlers.forEach(item => {
chains.unshift(item.fulfilled, item.rejected)
})
this.interceptors.response.handlers.forEach(item => {
chains.push(item.fulfilled, item.rejected);
});
while (chains.length) {
promise = promise.then(chains.shift(), chains.shift());
}
return promise;
}
CancelToken.js
- 闭包的经典应用
- 将
promise
的控制权交于外界 使用者控制 - 使用者一旦调用方法
promise
就可以自动触发promise.resolve
- 随后只要在
xhr
网络请求中 调用xhr.abort()
来中断请求1
2
3
4
5
6
7export function CancelToken(executor) {
var resolvePromise;
this.promise = new Promise((resolve) => {
resolvePromise = resolve
});
executor(() => resolvePromise());
}
参考链接
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LiuYuanhua!
评论