当前位置: 首页 > Web前端 > HTML

Axios源码解读——request

时间:2023-03-28 17:52:59 HTML

Axios是一个基于promise的网络请求库,运行在node.js和浏览器中。它是同构的(即相同的代码可以在浏览器和node.js中运行)。在服务器端它使用原生的node.jshttp模块,在客户端(浏览器端)它使用XMLHttpRequests。从Axios的官方介绍可以知道,这是一个可以同时运行在浏览器客户端+Node服务器上的网络请求库。在浏览器运行时,使用XMLHttpRequests构建请求,在Node环境中使用node的http模块构建网络。问。今天,我们就来解读一下axios的源码实现。解读完成后,我们来实现一个简单的axios库。我们先来看看axios库的项目目录结构。(如下图)从上图可以得到两条信息:axios的核心文件是lib/axios,所以如果我们只关注axios的运行时,只需要看文件在lib目录中。Axios仅依赖第三方库follow-redirects。该库用于处理HTTP重定向请求。axios的默认行为是遵循重定向。可以猜到这个库是用来跟随重定向的。—如果您不想自动跟随重定向,您需要显式声明maxRedirects=0。axios的官方文档我在百度上没有找到,这里抄一份axios的官方文档,大家可以参考一下。lib/axios我们打开lib/axios.js文件看一下。(如下图)重点关注这几行核心。|线条|说明||----|----||第26行|文档中的axios.create调用了createInstance函数,会创建一个新的axios实例||第34行|创建了一个默认的axios实例||第37~52行|默认的axios实例增加了很多属性和方法||第57行|导出默认的axios实例|axios接下来我们看一下axios类,它是axios源码的核心部分,位于lib/core/Axios.js。functionAxios(instanceConfig){this.defaults=instanceConfig;this.interceptors={request:newInterceptorManager(),response:newInterceptorManager()};}axios接收到配置,将instanceConfig配置保存在axios.defaults属性中,以供后续使用请求。同时通过InterceptorManager管理请求拦截器和响应拦截器。后面我们会讲到这个拦截管理器——InterceptorManager。axios的默认实例将使用lib/defaults.js中的配置创建。从axios的设置我们就知道了这部分文档修改配置的原因。(如下图所示)我们还可以看到,axios实例是axios进行网络请求-默认配置的最小单位,并没有一套所有实例共享的“全局默认配置”。但我们可以通过两种方式做到这一点。其中一种方法是我们为每个实例写两套配置,一套全局默认配置,一套个性化配置,在创建axios实例时手动合并。当然还有更聪明的办法,我们先看看之前P1的createInstance方法。函数createInstance(defaultConfig){varcontext=newAxios(defaultConfig);varinstance=bind(Axios.prototype.request,context);//...instance.create=functioncreate(instanceConfig){//这里通过闭包继承defaultConfig配置,新创建的实例会继承原实例的配置returncreateInstance(mergeConfig(defaultConfig,instanceConfig));};returninstance;}从代码可以看出,createInstance方法内部通过闭包继承了defaultConfig配置,新创建的实例会继承原实例的配置。这种情况下,我们也可以先创建一个带有一组全局默认配置的axios实例,然后使用这个axios实例调用axios/instance.create方法创建其他的axios实例,这样所有的axios实例都可以继承全局默认配置。InterceptManager-InterceptorManager下面我们来看一下InterceptorManager,axios内部的拦截器管理器。(如下图所示)InterceptorManager的整体实现还是比较简单的。里面有一个handler数组,里面存放了所有通过use方法注册的拦截器。use方法返回handlers.length-1作为拦截器id,调用eject方法时会将id下标对应的拦截器置为null。forEach方法遍历所有拦截器方法,执行传入的fn回调函数,将拦截器作为参数传入fn。由此可见,axios内部的职责分工还是比较明确的。InterceptorManager只负责收集和管理拦截器,并不关心拦截器的执行逻辑。但是,我觉得forEach方法的设计有些多余。如果我要设计它,我可能只会公开一个用于外部访问处理程序的getter方法。作者在这里的设计可能还有一些其他的考虑,我暂时还没有想到。request接下来我们来看axios类的核心方法,也就是request方法。axios使用request方法发起真正的网络请求。这段代码比较长,我会逐行分析这一大段代码。request方法中拦截器和请求的处理很优雅,我会重点介绍。比较简单的部分,我直接用表格来介绍(如下)|行数|说明||----|----||第32~41行|请求||第46~52行|设置请求方式,如果没有声明,则使用默认配置的请求方式,如果没有设置默认请求方式,则使用get请求|接下来我们重点关注拦截器的处理,我们先看RequestInterceptors。这里有两个参数没有在文档中描述。这里我们解释一下:第58行:使用use注册拦截器时,会先调用第三个参数中的options.runWhen方法。如果该方法返回false,则跳过请求拦截器。第62行:使用use注册拦截器时,第三个参数中的options.synchronous参数会明确声明拦截器是同步的,否则默认为异步拦截器,通过promise调用。——其实我觉得这个参数没什么意义,统一异步就好了。可能作者还考虑了一些其他的同步场景,暂时还没有想到。重要提示:第??64行使用unshift方法将请求拦截器按照注册的逆序添加到requestInterceptorChain中,供后续执行。这也意味着,请求拦截器对同一个配置的修改,后面添加的拦截器无法覆盖前面的拦截器。我们来看看下面的请求拦截器案例。后面设置的拦截器好像没有生效。看了源码我们可以看到,其实是执行顺序造成的。axios之所以这样设计,可能是为了防止滥用请求拦截器导致配置被后续处理器覆盖。——但是这个在文档中没有说明。如果碰巧发生这种情况,难免会引起一些混乱。而responseinterceptor的处理就简单多了,相信应该不用我多解释了吧。(如下图所示)值得注意的是拦截器在内部拦截器数组中同时添加了成功处理和错误处理,也就是说数组内部是这样的:['拦截器成功处理处理函数','拦截器错误处理函数','拦截器成功处理函数','拦截器错误处理函数',...]理解这个数据结构有助于理解最后一个核心代码的实现(如下图所示)).我们需要将每一行代码逐行解析:|行数|说明||----|----||第74行|判断是否为异步请求拦截器(默认:yes)||第75行|array,数组第一个元素是发起请求的方法(可以简单理解为fetch方法),第二个元素undefined用于补第82行的数字||第77行|将所有请求拦截器添加到链的开头||第78行|将所有响应拦截器添加到链的末尾||第80~83行|使用config构建第一个Promise,然后依次执行链条——请求拦截器->真实请求->响应拦截器,每次执行传入一个成功处理函数(作为resolve)和一个失败处理函数(作为reject)|的最后一条链的执行,非常优雅的解释了axios的内部工作流程,即核心工作流程的请求拦截器->真实请求->响应拦截器集合。建议大家仔细看看最后一段函数的处理,仔细看。下面还有一段是关于同步请求拦截器的处理,基本相同。感兴趣的童鞋可以自行阅读。总结的很好。至此,axios的基本结构和核心工作流程分析完毕。下一章我会详细分析真正的请求——dispatchRequest,请继续关注。最后一点,如果你已经看过这篇文章,希望你在离开前点赞~你的点赞是对作者最大的鼓励,也会让更多人看到这篇文章!如果您觉得本文对您有帮助,请帮忙点亮github上的star,鼓励一下!