axios create拦截_Axios 源码浅读

d99257632f95deb00c507a1e5a5247ab.png

axios 是一个基于 Promise 的 HTTP 工具库,可以运行在浏览器和 Node.js 上。代码量少,读起来不会花费太多时间。

初次阅读,不用面面俱到,重点是理解主要流程。为此,本文摘抄的 axios 源码经过大幅删减,只保留主干逻辑。

首先认识一下 axios 的主要目录及其作用。

目录说明
lib/源码根目录
lib/adapters跨平台的适配器
lib/cancel定义 CancelToken
lib/core核心领域模块
lib/helpers与核心领域无关的辅助工具
dist/编译输出目录
test/测试用例

下一步是找到入口文件。从 package.json 的 build 命令开始,顺藤摸瓜,最终在 webpack.config.js 中定位到入口文件:index.js

然而 index.js 只有一行代码:

module.exports = require('./lib/axios');

用户经常使用的 api 函数,统统在 lib/axios.js 中定义。

// Axios 定义构造函数var Axios = require('./core/Axios');// defaults 定义 config 的默认值var defaults = require('./defaults');function createInstance(defaultConfig) { ... }var axios = createInstance(defaults);axios.Axios = Axios;axios.create = function(instanceConfig) { ... }// 定义了取消请求的三个对象axios.Cancel = require('./cancel/Cancel');axios.CancelToken = require('./cancel/CancelToken');axios.isCancel = require('./cancel/isCancel');module.exports = axios;

axios 是 Axios 类型的一个实例,它的变量和行为在 lib/core/Axios.js 中定义如下:

var InterceptorManager = require('./InterceptorManager');function Axios(instanceConfig) {    this.defaults = instanceConfig;    this.interceptors = {        request: new InterceptorManager(),        response: new InterceptorManager(),    }}// 每次请求都会执行一次 request() 方法Axios.prototype.request = function(config) { ... }module.exports = Axios;

在 Axios 构造函数中,为每个实例创建 defaults 和 interceptors 属性。前者储存实例级别的 config 参数,后者是空的拦截器。拦截器下又有两个成员,分别对应请求拦截器和响应拦截器。

我们来看看拦截器是如何实现的(lib/core/InterceptorManager.js)。

// 构造函数,定义拦截器数组 handlersfunction InterceptorManager() {    this.handlers = [];}// 每次执行 use() 都会向拦截器数组增加两个处理函数// 前者处理 then() 回调,后者处理 catch() 回调InterceptorManager.prototype.use = function(f, r) {    this.handlers.push({        fulfilled: f,        rejected: r,    });    return this.handlers.length - 1;}// 遍历拦截器数组,每个元素执行一次 fnInterceptorManager.prototype.forEach = function(fn) {    forEach(this.handlers, function(h) {        if (h !== null) {            fn(h);        }    });}

拦截器的实现相对简单,只是定义了一个数组,暴露了 use() 和 forEach() 两个方法。

回到 Axios,看看最核心的函数 request() 方法会进行什么操作。

var dispatchRequest = require('./dispatchRequest');Axios.prototype.request = function(config) {    var chain = [dispatchRequest, undefined];    var promise = Promise.resolve(config);        this.interceptors.request.forEach((interceptor) => {        chain.unshift(interceptor.fulfilled, interceptor.rejected);    });        this.interceptors.response.forEach((interceptor) => {        chain.push(interceptor.fulfilled, interceptor.rejected);    });        while (chain.length) {        promise = promise.then(chain.shift(), chain.shift());    }        return promise;}

从上面代码可以看出,request() 方法会生成一条长长的 promise 调用链。链的头部是请求拦截器,中部为真正的请求动作(dispatchRequest),尾部是响应拦截器。

真正的请求动作(lib/core/dispatchRequest.js)定义了统一的响应处理逻辑。这些处理逻辑相对高层,与底层的环境无关。主要逻辑如下:

module.exports = function dispatchRequest(config) {    // 如果取消请求,则抛出异常,后面的 promise 直接走 catch() 分支    throwIfCancellationRequested(config);        var adapter = config.adapter || defaults.adapter;    return adapter(config).then((response) => {        throwIfCancellationRequested(config);                response.data = transformData(...);        return response;    }, (reason) => {        return Promise.reject(reason);    });}

axios 在浏览器使用 XMLHttpRequest 发送 ajax 请求,在 Node.js 使用 http 模块。这些和运行环境相关的平台差异,被 axios 使用 adapter(适配器) 抹平了。

默认情况下,会使用 lib/defaults.js 定义的默认适配器。默认适配器会根据平台的特征差异,自动选择对应的适配器。具体逻辑如下:

function getDefaultAdapter() {    var adapter;    if (typeof XMLHttpRequest !== 'undefined') {        adapter = require('./adapter/xhr');    } else if (typeof process !== 'undefined') {        adapter = require('./adapter/http');    }        return adapter;}var defaults = {    adapter: getDefaultAdapter(),};module.exports = defaults;

目前 axios 的适配器有两个,分别是 lib/adapters/xhr.js 和 lib/adapters/http.js,对应着浏览器环境和 node.js 环境。它们的作用类似,都是把原生的 callback 类型回调,封装为 Promise 异步风格,方便链式调用。

如果要向其他平台移植 axios(比如各种各样的小程序平台),只需为不同平台增加对应的适配器即可

(完)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值