上一章我们介绍了 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 = 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
配置删除。
第 56 ~ 58 行 —— 发起真实请求
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
源码解读系列收官。
最后一件事
如果您已经看到这里了,希望您还是点个赞再走吧~
您的点赞是对作者的最大鼓励,也可以让更多人看到本篇文章!
如果觉得本文对您有帮助,请帮忙在 github 上点亮 star
鼓励一下吧!