上一章我们介绍了 Axios 源码解读 —— request 篇,这一章我们来介绍 Axios 实际发起网络请求的部分吧,也就是 dispatchRequest
方法。
dispatchRequest
这个方法定义也比较简单(如下图)
文章图片
第 29 行 —— 取消请求
我们来逐行解析每一行代码所做的事情吧,首先是第 29 行的取消请求。(如下)
throwIfCancellationRequested(config);
这个动作不仅仅在发起正式请求前做了一次,而且在请求成功和请求失败时都做了一次。
只要是被
cancel
的请求,都是不会进入到成功或失败回调处理中的。(如下图)文章图片
而
throwIfCancellationRequested
函数所做的事情,就是检测该请求是否被取消,如果被取消则抛出一个错误,并阻止代码继续向下执行。(如下)function throwIfCancellationRequested(config) {
if (config.cancelToken) {
// 检测请求是否被取消,然后决定是否抛出错误
config.cancelToken.throwIfRequested();
}if (config.signal && config.signal.aborted) {
// 抛出错误
throw new Cancel('canceled');
}
}
当然,整套
CancelToken
的实现还是有一些复杂的(复杂在回调处理),如果有人感兴趣的话,可以单独讲讲这一部分的处理,这里就先不做展开了。第 35 ~ 40 行 —— 处理请求 data
config.data = https://www.it610.com/article/transformData.call(
config,
config.data,
config.headers,
config.transformRequest
);
这里用到了一个
transformData
方法,主要的作用是合并 data
,并且可以使用配置的 transformRequest
方法进行合并(如下图)。文章图片
而
transformData
方法就是遍历传入的 transformRequest
方法,将 data
和 headers
作为 transformRequest
的入参,然后将返回结果复制给 config.data
,作为最后请求要发送的 data
内容。第 43 ~ 54 行 —— 合并请求 headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
上面的代码主要做了两件事情:
- 第一件事情就是将通用的
headers
(common headers
)和对应方法的headers
以及原headers
做了一个合并; - 第二件事情就是在合并完成后,将多余的
headers
配置删除。
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
// ...
});
这里使用了配置的
adapter
,这其实就是发起请求的方法。文章图片
比如两个默认的请求方法,在浏览器端运行时使用的是
XMLHttpRequest
,在 Node
端运行时使用的是 http
模块。知道这一项配置可以做什么呢?
你可以在这里传入一个自定义的请求方法,例如在客户端使用
fetch
封装,而不是 XMLHttpRequest
。你也可以在这里传入一个你封装好的
mock
方法,返回的都是本地 json
数据,用于 mock
数据,这样你就不用额外搭建一个 mock
服务啦。......
言归正传,我们还是来看看
axios
默认的两个 adapter
吧,本文会重点讲解客户端 adapter
—— xhrAdapter
。客户端 adapter —— xhrAdapter 我们按照惯例,对客户端
adapter
—— xhrAdapter
逐行进行解析。发起请求前的准备工作
文章图片
行数 | 描述 |
---|---|
第 16 ~ 18 行 |
收集请求数据信息(requestData )、请求头信息(requestHeaders )、返回体类型(responseType ) |
第 19 ~ 28 行 |
对 cancelToken 做处理,在请求完成的时候取消这个订阅,释放内存;还有对 signal 的处理,这个 signal 在文档中已经看不到了,应该也是用于 abort 请求的。 |
第 30 ~ 32 行 |
对于 FormData 的请求移除 Content-Type 请求头,让浏览器自动设置请求头。 |
文章图片
首先在第
34
行,创建了一个 XMLHttpRequest
实例,用于后续的网络请求。然后在第
37 ~ 41
行,设置了用于 HTTP basic authentication
鉴权的信息,从这一段我们可以学到简单的 HTTP basic authentication
鉴权知识。首先对
password
进行 encodeURLComponent
转义编码,然后再将用户名与密码按照规则拼接后,使用了 btoa
将 用户名与密码
转成了 base64
编码。如果以后你要自己做这类事情的话,可以再翻到这一章节找到这部分的代码内容,抄一遍。
拼接请求 url
文章图片
首先是用
buildFullPath
方法拼接了完整的请求 url
。(如下图)文章图片
可以看到,该方法对绝对路径的
url
(isAbsoluteURL()
) 是不会拼接 baseURL
的。然后,
axios
还用 buildURL
方法将 params
参数拼接到了 url
中 —— 也就是我们常说的 query
参数。(如下)function buildURL(url, params, paramsSerializer) {
/*eslint no-param-reassign:0*/
if (!params) {
return url;
}var serializedParams;
if (paramsSerializer) {
serializedParams = paramsSerializer(params);
} else if (utils.isURLSearchParams(params)) {
serializedParams = params.toString();
} else {
// ...serializedParams = parts.join('&');
}if (serializedParams) {
// ...
// 在这里,将处理后的参数作为 query 查询参数拼接到 url 中
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
}return url;
};
这也是为什么使用
axios
时,GET
方法的参数要设置在 params
字段中。然后,使用
request.open
方法初始化了一个请求。响应回调函数
接下来就是比较核心的响应回调函数了(如下图)
文章图片
行数 | 描述 |
---|---|
第 54 行 |
获取所有响应头 |
第 55 行 |
获取响应内容 |
第 57 ~ 64 行 |
设置 promise resolve 内容,就是你经常 console.log 出来的那些东西,你应该很眼熟 |
后面基本上都是设置
XMLHttpRequest
对象的一些回调函数了。(如下图)文章图片
比较晚入行接触前端的,可能对
XMLHttpRequest
实例这些事件所做的事情不太清楚,可以参考一下 XMLHttpRequest 文档。最后,发送这个请求。(如下)
request.send(requestData);
回到 dispatchRequest
从上面可以看出,最后
dispatchRequest
调用 adapter
后,拿到了下面这些数据。{
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};
然后我们来看看
dispatchRequest
内部是如何处理这一段数据的吧。(如下图)文章图片
行数 | 描述 |
---|---|
第 59/72 行 |
处理被 CancelToken 取消的请求,如果请求已取消则不继续向下执行 |
第 62~67 行 |
将响应结果通过 transformResponse 进行转换,得到处理后的响应数据 |
第 69 行 |
将响应结果返回 |
axios
的请求流程就梳理清晰了,我们画一张流程图来梳理一下。(如下图)文章图片
这里对
Node
端的 adapter
就不进行展开讲解了,和客户端的差异主要在于下面几点:- 使用了
Node
的http
模块发起请求。 - 可以通过
proxy
设置代理服务器,请求将会发送到代理服务器。
axios
源码的解读就到这里为止了。可以看出,
axios
虽然是目前最流行的、比较强大的网络请求框架,但是源码看起来还是比较清爽易读的,建议大家可以自己按照文章的思路去看看。下一章,我们会实现一个简易的
axios
框架,包含 axios
的一些核心功能,将 axios
源码解读系列收官。最后一件事 如果您已经看到这里了,希望您还是点个赞再走吧~
您的点赞是对作者的最大鼓励,也可以让更多人看到本篇文章!
【Axios 源码解读 —— 网络请求篇】如果觉得本文对您有帮助,请帮忙在 github 上点亮
star
鼓励一下吧!