在 Axios 中设置拦截器很简单,通过 axios.interceptors.request 和 axios.interceptors.response 对象提供的 use 方法,就可以分别设置请求拦截器和响应拦截器:

// 添加请求拦截器
axios.interceptors.request.use(function (config) {config.headers.token = 'added by interceptor';return config;
});// 添加响应拦截器
axios.interceptors.response.use(function (data) {data.data = data.data + ' - modified by interceptor';return data;
});

在看具体的代码之前,我们先来分析一下它的设计思路。Axios 的作用是用于发送 HTTP 请求,而请求拦截器和响应拦截器的本质都是一个实现特定功能的函数。

我们可以按照功能把发送 HTTP 请求拆解成不同类型的子任务,比如有用于处理请求配置对象的子任务,用于发送 HTTP 请求的子任务和用于处理响应对象的子任务。当我们按照指定的顺序来执行这些子任务时,就可以完成一次完整的 HTTP 请求。

了解完这些,接下来我们将从 「任务注册、任务编排和任务调度」 三个方面来分析 Axios 拦截器的实现。

任务注册

要搞清楚任务是如何注册的,就需要了解 axios 和 axios.interceptors 对象。

/*** Create an instance of Axios** @param {Object} defaultConfig The default config for the instance* @return {Axios} A new instance of Axios*/
function createInstance(defaultConfig) {var context = new Axios(defaultConfig);var instance = bind(Axios.prototype.request, context);// Copy axios.prototype to instanceutils.extend(instance, Axios.prototype, context);// Copy context to instanceutils.extend(instance, context);return instance;
}// Create the default instance to be exported
var axios = createInstance(defaults);// Expose Axios class to allow class inheritance
axios.Axios = Axios;

bind函数:

module.exports = function bind(fn, thisArg) {return function wrap() {var args = new Array(arguments.length);for (var i = 0; i < args.length; i++) {args[i] = arguments[i];}return fn.apply(thisArg, args);};
};

在 Axios 的源码中,我们找到了 axios 对象的定义,很明显默认的 axios 实例是通过 createInstance 方法创建的,该方法最终返回的是Axios.prototype.request 函数对象。同时,我们发现了 Axios的构造函数:

/*** Create a new instance of Axios** @param {Object} instanceConfig The default config for the instance*/
function Axios(instanceConfig) {this.defaults = instanceConfig;this.interceptors = {request: new InterceptorManager(),response: new InterceptorManager()};
}

在构造函数中,我们找到了 axios.interceptors 对象的定义,也知道了 interceptors.request 和 interceptors.response 对象都是 InterceptorManager 类的实例。因此接下来,进一步分析InterceptorManager 构造函数及相关的 use 方法就可以知道任务是如何注册的:

function InterceptorManager() {this.handlers = [];
}/*** Add a new interceptor to the stack** @param {Function} fulfilled The function to handle `then` for a `Promise`* @param {Function} rejected The function to handle `reject` for a `Promise`** @return {Number} An ID used to remove interceptor later*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {this.handlers.push({fulfilled: fulfilled,rejected: rejected});return this.handlers.length - 1;
};

通过观察 use 方法,我们可知注册的拦截器都会被保存到 InterceptorManager 对象的 handlers 属性中。下面我们用一张图来总结一下 Axios 对象与 InterceptorManager 对象的内部结构与关系:

任务编排

现在我们已经知道如何注册拦截器任务,但仅仅注册任务是不够,我们还需要对已注册的任务进行编排,这样才能确保任务的执行顺序。这里我们把完成一次完整的 HTTP 请求分为处理请求配置对象、发起 HTTP 请求和处理响应对象 3 个阶段。

接下来我们来看一下 Axios 如何发请求的:

axios({url: '/hello',method: 'get',
}).then(res =>{console.log('axios res: ', res)console.log('axios res.data: ', res.data)
})

通过前面的分析,我们已经知道 axios 对象对应的是 Axios.prototype.request 函数对象,该函数的具体实现如下:

Axios.prototype.request = function request(config) {/*eslint no-param-reassign:0*/// Allow for axios('example/url'[, config]) a la fetch APIif (typeof config === 'string') {config = arguments[1] || {};config.url = arguments[0];} else {config = config || {};}config = mergeConfig(this.defaults, config);// Set config.methodif (config.method) {config.method = config.method.toLowerCase();} else if (this.defaults.method) {config.method = this.defaults.method.toLowerCase();} else {config.method = 'get';}// Hook up interceptors middlewarevar chain = [dispatchRequest, undefined];var promise = Promise.resolve(config);this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {chain.unshift(interceptor.fulfilled, interceptor.rejected);});this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {chain.push(interceptor.fulfilled, interceptor.rejected);});while (chain.length) {promise = promise.then(chain.shift(), chain.shift());}return promise;
};

参考资料:

Promise.resolve() - JavaScript | MDN

Promise.prototype.then() - JavaScript | MDN

任务编排的代码比较简单,我们来看一下任务编排前和任务编排后的对比图:

任务调度

任务编排完成后,要发起 HTTP 请求,我们还需要按编排后的顺序执行任务调度。在 Axios 中具体的调度方式很简单,具体如下所示:

  while (chain.length) {promise = promise.then(chain.shift(), chain.shift());}return promise;

因为 chain 是数组,所以通过 while 语句我们就可以不断地取出设置的任务,然后组装成 Promise 调用链从而实现任务调度,对应的处理流程如下图所示:

下面我们来回顾一下 Axios 拦截器完整的使用流程:

axios.interceptors.request.use(function (config) {config.headers.token = 'added by interceptor';return config;
});// 添加响应拦截器 —— 处理响应对象
axios.interceptors.response.use(function (data) {data.data = data.data + ' - modified by interceptor';return data;
});axios({url: '/hello',method: 'get',
}).then(res =>{console.log('axios res.data: ', res.data)
})

介绍完 Axios 的拦截器,我们来总结一下它的优点。Axios 通过提供拦截器机制,让开发者可以很容易在请求的生命周期中自定义不同的处理逻辑。

此外,也可以通过拦截器机制来灵活地扩展 Axios 的功能,比如 Axios 生态中列举的 axios-response-logger 和 axios-debug-log 这两个库。

参考 Axios 拦截器的设计模型,我们就可以抽出以下通用的任务处理模型:

axios 拦截器分析相关推荐

  1. axios拦截器_78.1K 的 Axios 项目有哪些值得借鉴的地方

    Axios 是一个基于 Promise 的 HTTP 客户端,同时支持浏览器和 Node.js 环境.它是一个优秀的 HTTP 客户端,被广泛地应用在大量的 Web 项目中. 由上图可知,Axios ...

  2. vue路由守卫,axios拦截器,权限树

    K15项目案例-后台 1.分页问题 关于分页表格列中的序号问题: <el-table-column type="selection" label="序号" ...

  3. 5 拦截器拦截请求路由_手写简易版axios拦截器,实现微信小程序wx.request的封装与拦截...

    前言: axios是一个功能强大的网络请求库,其中拦截器又是axios的精髓.在小程序的开发或者需要手动实现ajax的时候,没有实现对请求的拦截,开发的时候非常不方便,因此手写一个简易版的axios拦 ...

  4. axios vue 加载效果动画_vue中使用axios拦截器实现数据加载之前的loading动画显示 @劉䔳...

    首先新建一个 loading.vue组件,写loading动画效果 .loader { width: 100%; height: 100%; display: flex; align-items: c ...

  5. [js] axios拦截器原理是什么?

    [js] axios拦截器原理是什么? 拦截器原理其实就是用use添加用户自定义的函数到拦截器的数组中. 最后把他们放在拦截器请求前,请求后.组成promise链式调用. // 组成`Promise` ...

  6. 使用Axios拦截器打印前端请求日志和后端后返回日志

    在main.ts引入 import axios from 'axios'; axios.defaults.baseURL = process.env.VUE_APP_SERVER;/*** axios ...

  7. Vue2学习小记-给Vue2路由导航钩子和axios拦截器做个封装

    1.写在前面 最近在学习Vue2,遇到有些页面请求数据需要用户登录权限.服务器响应不符预期的问题,但是总不能每个页面都做单独处理吧,于是想到axios提供了拦截器这个好东西,再于是就出现了本文. 2. ...

  8. 【vue开发问题-解决方法】(八)利用axios拦截器实现elementUI中加载动画,控制加载区域

    [vue开发问题-解决方法](八)利用axios拦截器实现elementUI中加载动画,控制加载区域 参考文章: (1)[vue开发问题-解决方法](八)利用axios拦截器实现elementUI中加 ...

  9. Vue2学习小记-给Vue2路由导航钩子和axios拦截器做个封装 1

    1.写在前面 最近在学习Vue2,遇到有些页面请求数据需要用户登录权限.服务器响应不符预期的问题,但是总不能每个页面都做单独处理吧,于是想到axios提供了拦截器这个好东西,再于是就出现了本文. 2. ...

最新文章

  1. 独家 | 经验教训分享:我的第一个机器学习项目
  2. 收集的48个Shell脚本小技巧
  3. python中的.idea文件夹是干嘛的
  4. 计算机一级b考试教程,全国计算机等级考试一级B教程
  5. centos清楚缓存
  6. ubuntu安装sublime3并配置python3环境
  7. cout 数组_C++数组指针!
  8. SAP License:作业费用分割均分常见原因
  9. Springboot构建Echarts数据可视化
  10. 苹果5s现在还能用吗_苹果ios稳定企业签名,现在苹果企业签名还是最稳定的苹果签名吗...
  11. Java基础之访问文件与目录——获取与文件存储有关的信息(GetFileStores)
  12. mac上virtualbox创建vm需要注意启动顺序
  13. 5G到底厉害在什么地方?和4G有什么不同?
  14. 安装ubuntu黑屏,修复分区表
  15. javaScript入门,新手小白也能会
  16. 使用mybaisplus时使用LambdaQueryWrapper进行条件查询发生evaluating expression异常处理
  17. 利用js快速完成大学生新生安全教育课程
  18. Mybatis generator mapper文件重新生成不会覆盖原文件
  19. Excel实用教程-字符提取函数(FIND/LEFT/RIGHT/LEN/MID/TEXT)
  20. 人民币对美元汇率中间价报6.7615元 下调73个基点

热门文章

  1. 解决 Windows server 2008 R2 DataCenter 不能安装 .NETFramework4.6.1
  2. Excel引用其他工作表数据的几种方法
  3. zabbix不支持mysql8_zabbix问题集锦
  4. fastreport返回是否打印成功_我国首次完成太空“3D打印”,都打印了什么?
  5. 区块链通证的作用-AngelToken的价值
  6. Buffalo WHR-G300N V2刷DD-WRT+Wiwiz全过程记录
  7. ASP.NET MVC模式下使用fluentscheduler+stmp定时自动发送邮件
  8. 计算机高级应用与速录,目前最优秀计算机速录是什么软件?
  9. easyuefi只能在基于uefi启动的_关于UEFI安装的普遍误区
  10. srs流媒体服务器性能,srs流媒体服务器 配置