Axios构造函数
Axios构造函数
Axios构造函数实例化出的axios对象拥有拦截器属性,拦截器属性里有两个拦截器对象,一个是请求拦截器,一个是响应拦截器
下面是结构图
然后就是整个Axios最核心的东西,Axios.prototype.request方法,使用axios发请求就是在调用这个方法。
Axios.prototype.request会将请求拦截器,请求,响应拦截器放在一个数组里,形成一个队列,然后按照顺序用Promise.ptotorype.then方法调用,then方法的返回值还是一个promise,因此就形成一个then链,直到队列中所有操作都执行结束,然后将最终的promise返回。这就是Axios的核心实现。
下面是then链的流程图:
下面是Axios.js源代码:
'use strict'; var utils = require('./../utils'); var buildURL = require('../helpers/buildURL'); var InterceptorManager = require('./InterceptorManager'); var dispatchRequest = require('./dispatchRequest'); var mergeConfig = require('./mergeConfig'); /** * Create a new instance of Axios * * @param {Object} instanceConfig The default config for the instance */ function Axios(instanceConfig) { this.defaults = instanceConfig;//axios实例上的defaults属性,存放默认设置 this.interceptors = {//axios上的拦截器属性,是一个对象,里面分别存放请求和相应的拦截器对象 //InterceptorManager对象在添加了拦截器之后,里面的一个拦截器其实是存了拥有两个属性的对象,fulfilled和rejected,分别是promise成功和失败时的回调函数 request: new InterceptorManager(), response: new InterceptorManager() }; } /** * Dispatch a request * * @param {Object} config The config specific for this request (merged with this.defaults) */ //发出请求 Axios.prototype.request = function request(config) { /*eslint no-param-reassign:0*/ // Allow for axios('example/url'[, config]) a la fetch API if (typeof config === 'string') {//如果第一个参数config是字符串,说明是请求的url参数 config = arguments[1] || {};//第二个参数是请求的config config.url = arguments[0];//第一个参数赋值给config的url属性 } else { config = config || {};//否则config就是请求配置 } config = mergeConfig(this.defaults, config); //调用mergeConfig将默认设置和自定义设置合并,this.defaults上同名属性会覆盖config上的 config.method = config.method ? config.method.toLowerCase() : 'get'; //method配置转换成小写字母,如果没传递,默认是get // Hook up interceptors middleware var chain = [dispatchRequest, undefined]; //请求和拦截器的执行链 //执行链数组中的元素都是函数,两个为一组,下面的操作会将两个为一组传递给promise.then() //Promise.prototype.then接收两个参数,第一个是resolve,第二个是reject //then方法返回的是一个新的Promise实例,因此可以使用链式写法 var promise = Promise.resolve(config);//初始promise this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { //遍历this.interceptors.request.handlers数组,对其中的每一个拦截器执行unshiftRequestInterceptors函数 chain.unshift(interceptor.fulfilled, interceptor.rejected); //将请求拦截器插入到执行链的最开头位置 }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { //遍历this.interceptors.response.handlers数组,对其中的每一个拦截器执行pushResponseInterceptors函数 chain.push(interceptor.fulfilled, interceptor.rejected); //将响应拦截器插入到执行链的结尾位置 }); while (chain.length) { //循环执行链数组,将两个为一组传递给promise.then() //将执行链里的请求拦截器和dispatchRequest和响应拦截器按顺序执行完 promise = promise.then(chain.shift(), chain.shift()); } return promise;//返回链式promise最终返回的promise对象 }; Axios.prototype.getUri = function getUri(config) {//获取带有查询字符串的url config = mergeConfig(this.defaults, config); //调用mergeConfig将默认设置和自定义设置合并,this.defaults上同名属性会覆盖config上的 return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''); //返回带有查询字符串的url,将斜杠转换为空字符串 }; // Provide aliases for supported request methods //为以下类型请求提供别名调用方法,例如Axios.prototype.delete utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, config) { return this.request(utils.merge(config || {}, { method: method, url: url })); }; }); utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, data, config) { return this.request(utils.merge(config || {}, { method: method, url: url, data: data })); }; }); module.exports = Axios;
下面是InterceptorManager.js源代码:
'use strict'; var utils = require('./../utils'); function InterceptorManager() {//InterceptorManager构造函数 this.handlers = [];//存拦截器的数组属性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 */ //原型上的use方法,用于添加新的拦截器到InterceptorManager对象的handlers属性里 InterceptorManager.prototype.use = function use(fulfilled, rejected) { //fulfilled是promise的成功处理函数,rejected是promise的失败处理函数 this.handlers.push({ fulfilled: fulfilled, rejected: rejected }); return this.handlers.length - 1;//返回当前拦截器在handlers中的索引,便于之后移除 }; /** * Remove an interceptor from the stack * * @param {Number} id The ID that was returned by `use` */ //从handlers中删除拦截器,参数id是use方法返回的handlers数组索引 InterceptorManager.prototype.eject = function eject(id) { if (this.handlers[id]) {//如果handlers数组中存在id对应的拦截器,就赋值为null this.handlers[id] = null; } }; /** * Iterate over all the registered interceptors * * This method is particularly useful for skipping over any * interceptors that may have become `null` calling `eject`. * * @param {Function} fn The function to call for each interceptor */ //遍历所有已注册的拦截器 //这个方法在想要跳过被移除的拦截器的时候非常有用,因为调用eject后对应的拦截器被赋值为null //参数fn会在遍历的时候调用 InterceptorManager.prototype.forEach = function forEach(fn) { utils.forEach(this.handlers, function forEachHandler(h) { if (h !== null) {//如果拦截器不为null,传递给fn作为参数来调用 fn(h); } }); }; module.exports = InterceptorManager;
buildURL方法,创建一个带有查询字符串参数的url
'use strict'; var utils = require('./../utils'); function encode(val) {//encodeURIComponent转码后将一些特殊字符变回原样 return encodeURIComponent(val). replace(/%40/gi, '@'). replace(/%3A/gi, ':'). replace(/%24/g, '$'). replace(/%2C/gi, ','). replace(/%20/g, '+'). replace(/%5B/gi, '['). replace(/%5D/gi, ']'); } /** * Build a URL by appending params to the end * * @param {string} url The base of the url (e.g., http://www.google.com) * @param {object} [params] The params to be appended * @returns {string} The formatted url */ //创建一个url,将params参数也就是get请求参数连在原url的后面 module.exports = function buildURL(url, params, paramsSerializer) { /*eslint no-param-reassign:0*/ if (!params) {//如果没有params参数,直接返回原url return url; } var serializedParams; if (paramsSerializer) { //如果传递了paramsSerializer,就调用将params序列化,结果存入变量serializedParams //paramsSerializer是用户传递的自定义函数 serializedParams = paramsSerializer(params); } else if (utils.isURLSearchParams(params)) { //如果没有传递paramsSerializer,就判断params是否是URLSearchParams对象 //如果是,就调用URLSearchParams.toString(),将其变成字符串可以直接使用 serializedParams = params.toString(); } else {//既没有传递paramsSerializer也不是原生URLSearchParams对象,执行下面操作 var parts = [];//key=value形式的字符串 utils.forEach(params, function serialize(val, key) { //调用forEach循环params对象 if (val === null || typeof val === 'undefined') { //如果当前params的值是空,就不做操作 return; } if (utils.isArray(val)) {//判断当前params的值是否是一个数组 key = key + '[]';//如果是数组,key后面连上数组索引的方括号 } else { val = [val];//如果不是数组,当前值等于只有val一个元素的数组 } utils.forEach(val, function parseValue(v) { //循环val数组 if (utils.isDate(v)) {//如果当前值是日期对象 v = v.toISOString();//将日期对象转换成ISO格式字符串 } else if (utils.isObject(v)) {//又或者当前值是一个对象 v = JSON.stringify(v);//将对象字符串化 } parts.push(encode(key) + '=' + encode(v));//parts数组里插入key=value的字符串 }); }); serializedParams = parts.join('&');//将字符串之间用&连成一整个字符串 } if (serializedParams) { var hashmarkIndex = url.indexOf('#');//url中hash标记位置 if (hashmarkIndex !== -1) {//如果含有hash标记就去掉 url = url.slice(0, hashmarkIndex); } url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;//将基础url和查询字符串连接起来 } return url; };