手写Axios(上)

阅读建议

如果你对XMLHttpRequest的使用不熟悉,建议先在MDN上先行学习。

项目源码已经上传到github,并已添加注释,您可直接查看源码学习。

先来看axios的使用方式

1
2
3
4
5
6
7
8
9
10
axios({
method: 'post',
url: '/url',
data: {
a: 1,
b: 2
}
}).then((res) => {
console.log(res)
})

axios核心

XMLHttpRequest:具体使用参见XMLHttpRequest

1
2
3
4
5
6
7
// xhr.js
const request = new XMLHttpRequest()
request.open(method.toUpperCase(), url, true)
request.onreadystatechange = function(){
// 监听xhr状态改变
}
request.send(data)

数据处理

处理url

  1. 从config中获取到url和params
  2. 如果没有params选项,直接返回url
  3. 如果包含params,对params的key做遍历。如果key为空,忽略该params,如果key不为空,针对value的不同类型做对应处理。具体代码如下:
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
export function buildUrl(url: string, params: any): string {
if (!params) return url;

const parts: string[] = [];
Object.keys(params).forEach((key) => {
const val = params[key];
if (val === null || typeof val === 'undefined') {
return ''
}
let values = [];
if (Array.isArray(val)) {
values = val;
key += '[]'
} else {
values = [val]
}
values.forEach((val) => {
if (isDate(val)) {
val = val.toISOString()
} else if (isPlainObject(val)) {
val = JSON.stringify(val)
}
parts.push(`${encode(key)}=${encode(val)}`)
})
})

let serializedParams = parts.join('&');
if (serializedParams) {
const markIndex = url.indexOf('#')
if (markIndex !== -1) {
url = url.slice(0, markIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}

处理获取到的data信息

处理data信息比较简单,直接上代码:

1
2
3
4
5
6
export function transformRequest(data: any): any {
if (isPlainObject(data)) {
return JSON.stringify(data)
}
return data
}

处理头部信息

  1. 考虑兼容Content-Type大小写不同的情况,利用normalizeHeaderName函数进行规范化
  2. 如果data是对象,并且不存在Content-Type,给其配置默认值为'application/json;charset=utf-8',具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function normalizeHeaderName(headers: any, normalizedName: string): void {
if (!headers) return
Object.keys(headers).forEach(name => {
if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
headers[normalizedName] = headers[name]
delete headers[name]
}
})
}

export function processHeaders(headers: any, data: any): any {
normalizeHeaderName(headers, 'Content-Type')
if (isPlainObject(data)) {
if (headers && !headers['Content-Type']) {
headers['Content-Type'] = 'application/json;charset=utf-8'
}
}
return headers
}

处理返回信息

1
2
3
4
5
6
7
8
9
10
export function transformResponse(data: any): any {
if (typeof data === 'string') {
try {
data = JSON.parse(data)
} catch (e) {
// do nothing
}
}
return data
}

Promise化

  1. 处理xhr.js,把xhr核心包含在promise里面
  2. 当触发onreadystatechange事件的时候,格式化响应数据,并对状态进行判断,分别触发 resolve/reject事件