

Axios 源码解读 —— 网络请求篇
source link: https://segmentfault.com/a/1190000041288310
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

上一章我们介绍了 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
鼓励一下吧!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK