接下来,我们应该看一下Axios类中究竟做了什么,到底是怎么发送请求的。因为Axios类中应用到了Interceptor,所以我们要先看一下拦截器是怎么实现的。
很遗憾,至于发送请求的详细内容需要到dispatchRequest方法和xhr适配器中才能看到了,在本章节Axios类中我们只能说到调用dispatchRequest方法这一步,所以发送请求的详细内容要留到下一章节来说了,先来看一下本章节的内容吧。
InterceptorManager 先来看一下拦截器内容,源码位于lib/core/InterceptorManager.js。
构造函数 构造函数的内容太简单了,直接放下边吧:
1 2 3 function InterceptorManager ( ) { this .handlers = []; }
就是维护了一个handlers数组,用来收集拦截器。
原型方法 原型对象上有use,eject和forEach三个方法,其中最常用的就是use方法。
InterceptorManager.prototype.use
函数接收两个参数,分别是成功和失败的回调函数,最后函数会返回最后加入hadnlers中的拦截器的下标
1 2 3 4 5 6 7 InterceptorManager.prototype.use = function use (fulfilled, rejected ) { this .handlers.push({ fulfilled : fulfilled, rejected : rejected }); return this .handlers.length - 1 ; };
至于fulfilled和rejected函数应该做哪些内容,放到后边再说,这里只知道拦截器有什么就好了。
函数的返回值是用来配合eject方法使用的。
InterceptorManager.prototype.eject
eject方法接收一个下标,将handlers中对应下标的拦截器置为null。注意这里是置为null,而不是从数组中移除这个拦截器 。
1 2 3 4 5 InterceptorManager.prototype.eject = function eject (id ) { if (this .handlers[id]) { this .handlers[id] = null ; } };
我们在调用use方法后会得到拦截器的下标,可以使用这个下标通过eject方法来移除这个拦截器。所以在eject方法中是置为null,而不是移除元素,这样可以保证下标的准确性,不会因为调用eject方法导致拦截器的下标发生变化。
InterceptorManager.prototype.forEach
这个函数在Axios类中用到了,平常开发过程中我们一般是用不到这个函数的。
这个函数接收一个参数,这个参数也是一个函数,我们称之为fn。然后forEach函数内部会循环handlers中的所有拦截器,把每个拦截器作为参数传入fn执行。(这里会判断拦截器是否为null,因为我们移除拦截器会置为null)
1 2 3 4 5 6 7 InterceptorManager.prototype.forEach = function forEach (fn ) { utils.forEach(this .handlers, function forEachHandler (h ) { if (h !== null ) { fn(h); } }); };
InterceptorManager源码贴一下拦截器源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 'use strict' ;var utils = require ('./../utils' );function InterceptorManager ( ) { this .handlers = []; } InterceptorManager.prototype.use = function use (fulfilled, rejected ) { this .handlers.push({ fulfilled : fulfilled, rejected : rejected }); return this .handlers.length - 1 ; }; InterceptorManager.prototype.eject = function eject (id ) { if (this .handlers[id]) { this .handlers[id] = null ; } }; InterceptorManager.prototype.forEach = function forEach (fn ) { utils.forEach(this .handlers, function forEachHandler (h ) { if (h !== null ) { fn(h); } }); }; module .exports = InterceptorManager;
Axios类 看完了InterceptorManager,我们终于可以来看一下Axios类了。还是分为构造函数会原型对象方法来看。
构造函数 Axios类的构造函数也是很简单的,接收一个配置项,然后生成一些默认属性。
1 2 3 4 5 6 7 function Axios (instanceConfig ) { this .defaults = instanceConfig; this .interceptors = { request : new InterceptorManager(), response : new InterceptorManager() }; }
对于instanceConfig,不知道大家还能否记得第一章节也就是axios.js的内容,如果忘记了需要回顾一下哦。
在axios.js中我们通过new Axios创建实例,对于axios实例,这个instanceConfig就是defaults.js中的默认配置,而对于axios.create(config)创建的实例,这个instanceConfig是defaults.js和config合并后的配置内容。在构造函数中,我们把用于创建实例的配置项存储到this.defaults上。
Axios还维护有一个属性interceptors用来收集拦截器,我们知道拦截器构造函数的内部是维护了一个数组,所以这里Axios类中的interceptors对象的两个属性request和response,分别维护请求拦截器数组和响应拦截器数组。记住这里是维护了两个数组,后边还会说到他。
request方法 Axios.prototype.request是Axios类的一个核心方法了,在lib/axios.js中的createInstance方法中有这么一行代码var instance = bind(Axios.prototype.request, context);,关于这一行代码的内容我们说了挺多的,它的存在就是为了让我们方便的调用这个request方法
我们在调用request方法的时候可以传入一个参数,比如axios({method:'post',url:'/url'})这种。但是除了这种方法外request还有一种调用方式入axios.request('/url', config)。
先来看一下reuqest方法中对于config的处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 Axios.prototype.request = function request (config ) { if (typeof config === 'string' ) { config = utils.merge({ url : arguments [0 ] }, arguments [1 ]); } config = utils.merge(defaults, { method : 'get' }, this .defaults, config); config.method = config.method.toLowerCase(); }
对于config的处理我们需要注意两点,一是config为字符串的情况。而是对所有配置项对合并,注意配置项对优先级。参数config -> this.defaults -> {method:’get’} -> defaults
再看一下request方法中剩下的内容,我们一点点来说
1 2 3 4 5 6 7 8 9 10 11 12 var 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;
先说一下chain这个数组,这里维护的都是成对的,比如请求拦截器的成功回调和失败回调、响应拦截器的成功回调和失败回调。而dispatchRequest这个发送请求的方法比较特殊,所以加入了一个undefined与其组成一对。看到后边的调用方式就会理解这里的。
1 var chain = [dispatchRequest, undefined ];
定义了一个变量promise,赋值为Promise.resolve(config)。为什么传入config?,想一下我们的请求拦截器接收参数config并要求返回config是不是突然知道为什么传入config了,这里Promise.resolve(config)可以让我们的请求拦截器接收到config,而请求拦截器返回到config可以给后边的请求拦截器和dispatchRequest使用。
1 var promise = Promise .resolve(config);
接下来看一下请求拦截器的部分,我们收集请求拦截器肯定是每调用一次Interceptor.use就会往this.interceptors.request中push一个对象,其中有fulfilled和rejected方法。然后在这里我们循环存储请求拦截器的数组并依次将其fulfilled和rejected方法添加到chain的前边,所以最后一个定义的请求拦截器应该是chain的第一对元素 哦。
1 2 3 this .interceptors.request.forEach(function unshiftRequestInterceptors (interceptor ) { chain.unshift(interceptor.fulfilled, interceptor.rejected); });
响应拦截器差不多,只是往后追加了,所以最后一个定义的响应拦截器是chain的最后一对元素 。
1 2 3 this .interceptors.response.forEach(function pushResponseInterceptors (interceptor ) { chain.push(interceptor.fulfilled, interceptor.rejected); });
然后就是依次调用chain中的内容并将最终结果返回啦,返回结果也是一个Promise。
在调用过程中传入promise.then的依次是
每一组请求拦截器的成功和失败回调
dispathRequest和undefined
每一组响应拦截器的成功和失败回调
1 2 3 4 while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise;
在这里我们需要注意的是,请求拦截器是先定义的后执行,而响应拦截器是先定义的先执行。
request方法完整源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Axios.prototype.request = function request (config ) { if (typeof config === 'string' ) { config = utils.merge({ url : arguments [0 ] }, arguments [1 ]); } config = utils.merge(defaults, { method : 'get' }, this .defaults, config); config.method = config.method.toLowerCase(); var 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; };
其他扩展方法 我们都知道axios还给我们提供了指定请求方法的请求方式,比如axios.get()或者axios.post()等,其实这些方法都是基于Axios.prototype.request来实现的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 utils.forEach(['delete' , 'get' , 'head' , 'options' ], function forEachMethodNoData (method ) { Axios.prototype[method] = function (url, config ) { return this .request(utils.merge(config || {}, { method : method, url : url })); }; }); utils.forEach(['post' , 'put' , 'patch' ], function forEachMethodWithData (method ) { Axios.prototype[method] = function (url, data, config ) { return this .request(utils.merge(config || {}, { method : method, url : url, data : data })); }; });
思路一致,如果调用get等方法等时候传入了config就将config传入request方法,但是请求方法、url这些不从config中取,因为在调用的时候已经制定了请求方法且传入了请求url。
几个方法的实现是一样的,只是post/put/patch请求可以传递data
贴一下Axios类的源码内容吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 'use strict' ;var defaults = require ('./../defaults' );var utils = require ('./../utils' );var InterceptorManager = require ('./InterceptorManager' );var dispatchRequest = require ('./dispatchRequest' );function Axios (instanceConfig ) { this .defaults = instanceConfig; this .interceptors = { request : new InterceptorManager(), response : new InterceptorManager() }; } Axios.prototype.request = function request (config ) { if (typeof config === 'string' ) { config = utils.merge({ url : arguments [0 ] }, arguments [1 ]); } config = utils.merge(defaults, { method : 'get' }, this .defaults, config); config.method = config.method.toLowerCase(); var 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; }; utils.forEach(['delete' , 'get' , 'head' , 'options' ], function forEachMethodNoData (method ) { Axios.prototype[method] = function (url, config ) { return this .request(utils.merge(config || {}, { method : method, url : url })); }; }); utils.forEach(['post' , 'put' , 'patch' ], function forEachMethodWithData (method ) { Axios.prototype[method] = function (url, data, config ) { return this .request(utils.merge(config || {}, { method : method, url : url, data : data })); }; }); module .exports = Axios;
到现在,我们已经知道整个axios的执行流程,怎么处理配置项,请求拦截和响应拦截,返回最终的promise等这一个流程已经串下来了,现在就差了关键一步,dispatchRequest中关于发送请求的详细内容是怎么实现的,毕竟axios就是用来发送请求的,这里才是真正的核心内容。