axios源码解析-Axios对象

axios源码解析-Axios对象

之前说过,axios中,axios本身是一个函数,但它绑定了Axios构造函数的所有方法,那么,Axios这个构造函数究竟有那些属性和方法呢,在axios的源码中,有专门的一个文件Axios.js去编写这个构造函数,那么我们先来看一下源码中是如何写的

// 检验版本的对象
var validators = validator.validators;
/**
 * Create a new instance of Axios
 *
 * @param {Object} instanceConfig The default config for the instance
 */
// Axios的处理流程:先执行请求拦截器,
// 然后执行transfromDataResquest,xhr,transfromDataRespones(这三部分在dispatchRequest这个流程中执行)
// 最后执行响应拦截器
function Axios(instanceConfig) {
    this.defaults = instanceConfig;
    this.interceptors = {
        request: new InterceptorManager(),
        response: new InterceptorManager()
    };
}

可以看出,Axios这个对象本身非常简单,将参数设置为默认配置,然后将本身的拦截器通过InterceptorManager的实例化得到请求拦截器和响应拦截器,其实这也就是axios本身最重要的作用,在主体中只包含了最重要的骨架。之后,在Axios的原型链上挂在了有一些方法,其中最重要的是处理请求的rquest

Axios.prototype.request = function request(config) {
    /*eslint no-param-reassign:0*/
    // Allow for axios('example/url'[, config]) a la fetch API
    // 对传入配置的处理
    // 这里可以处理不同的请求形式:axios('url',config)或者axios(config)
    if (typeof config === 'string') {
        config = arguments[1] || {};
        config.url = arguments[0];
    } else {
        config = config || {};
    }
    // 完善并补充config配置(跟自己之前设置的default相比进行添加或覆盖,合并两个配置)
    config = mergeConfig(this.defaults, config);

    // Set config.method
    if (config.method) {
        // 自动将请求的方法转化为小写
        config.method = config.method.toLowerCase();
    } else if (this.defaults.method) {
        // 没有在配置中设置请求方法使用默认配置
        config.method = this.defaults.method.toLowerCase();
        // 没有设置请求方法且没有默认配置,默认使用get方法
    } else {
        config.method = 'get';
    }

    var transitional = config.transitional;
    // 进行版本检验,1.0.0版本后,transitional这个属性被移除
    if (transitional !== undefined) {
        validator.assertOptions(transitional, {
            silentJSONParsing: validators.transitional(validators.boolean, '1.0.0'),
            forcedJSONParsing: validators.transitional(validators.boolean, '1.0.0'),
            clarifyTimeoutError: validators.transitional(validators.boolean, '1.0.0')
        }, false);
    }

    // filter out skipped interceptors
    // 拦截器链
    var requestInterceptorChain = [];
    // 是否为同步函数
    var synchronousRequestInterceptors = true;
    this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
        if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
            return;
        }
        // 确保每个请求拦截器都是同步的,最终的拦截器才是同步的
        synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
        // 将每个请求拦截器的成功回调和失败回调绑定在执行链的最前端
        requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
    });

上面这部分是做了一些前置处理,比如对不同的请求格式和不同版本进行适配,而后面这部分对于链式调用的处理则是axios源码中最经典的一部分

    var responseInterceptorChain = [];
    // 注意这里的foreach方法是拦截器原型链上的foreach方法
    // 这个方法内部会遍历所有拦截器
    this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
        // 将每个响应拦截器加入到执行链的最后端
        responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
    });

    var promise;
    // 如果拦截器需要异步处理
    if (!synchronousRequestInterceptors) {
        // undefined用于占位,保证回调函数两个一组可以成对处理
        // 这里的dispatchRequest是处理请求的核心代码
        //  在dispatchRequest中会检验是否取消请求,适配器处理,并利用transfrom在发送请求前对数据进行处理
        var chain = [dispatchRequest, undefined];
        // 将整个请求拦截器添加到执行链的前端,将响应拦截器整个添加到执行链的后端
        Array.prototype.unshift.apply(chain, requestInterceptorChain);
        chain.concat(responseInterceptorChain);
        // 请求拦截器最先执行,可以拿到全部的config
        promise = Promise.resolve(config);
        while (chain.length) {
            // 两个为一组(正确和错误回调)逐个shift,解决回调地狱代码
            // 注意这里处理到dispatchRequest就会执行核心的异步请求,然后继续执行响应拦截器
            // 这里的参数也会利用promise逐层传递,由于链式调用的各个部分都是promise,相当于进行了链式调用
            promise = promise.then(chain.shift(), chain.shift());
        }
        // 最终返回一个promise,这个相当于是经过处理后,我们最后使用axios时的异步函数,它的。then也就是我们最后使用的then
        return promise;
    }
};

我们来梳理一下这部分的基本逻辑,axios将所有请求拦截器加入到了核心处理方法dispatchRequest的前面,把所有的响应拦截器加入到核心处理方法的后面,这里分了两种情况进行处理,如果有有拦截器是异步的方法,那么就调用异步的处理方法,将保存了所有方法的数组两个一组进行调用,前面的作为成功的回调函数,后一个作为失败的回调函数(这也是之前放入数组的顺序),当进行到dispatchRequest时,由于提前放入了一个undefined作为占位符,因此这时候只会处理dispatchRequest这个函数,之后再用同样的方式依此调用响应拦截器,最后返回一个Promise对象,这就是我们平时使用axios时拿到的promise对象,我们只要调用then方法就可以拿到最后经过响应拦截器处理后的返回结果。这个过程中,由于最开始定义的promise拿到了参数,因此在每个拦截器都会由于链式调用(这里相当于用数组的方式实现了链式调用)而拿到上一层处理后的参数。最终实现了用拦截器处理整个数据的目的
如果是响应拦截器和请求拦截器都是同步的函数的话,我们就没有必要使用更耗费性能的异步函数了,因此,对于同步的拦截器,也做了类似的处理
首先要注意,只有所有拦截器都是同步的,才能用同步进行处理,只要有一个拦截器是异步的,就会因为处理顺序混乱而产生错误。另外,同步拦截器只能处理请求拦截器的部分,因为dispatchRequest部分采用Promise封装AJAX,返回的是一个Promise对象,所以必须采用异步的方式处理响应拦截器

  // 当拦截器为同步时,进行处理(如果是异步的话拦截器执行链已经执行完毕)
    var newConfig = config;
    // 首先处理请求拦截器
    while (requestInterceptorChain.length) {
        var onFulfilled = requestInterceptorChain.shift();
        var onRejected = requestInterceptorChain.shift();
        try {
            // 递归,每次调用成功的回调函数更新配置
            newConfig = onFulfilled(newConfig);
        } catch (error) {
            // 否则调用处理异常的函数
            onRejected(error);
            break;
        }
    }
    // 注意在早期版本中。dispatchRequest是放在Promise中进行处理的,相当于在微任务中执行请求
    // 由于微任务的创建是在promise链构建之前,因此这种方式会增加进行真正请求前的处理时间
    //注意到这里用try-catch微任务不会过早创建,也就解决了微任务过早创建,宏任务过长或异步阻塞导致请求延时处理的情况
    // 相当于之前是先创建微任务,再执行请求,
    // 现在将请求放入宏任务中,先执行请求再创建微任务,利用微任务创建时间和网络通信时间的重合,缩短响应时间
    try {
        promise = dispatchRequest(newConfig);
    } catch (error) {
        return Promise.reject(error);
    }
    // 用同样的方式处理响应拦截器,使其链式调用
    while (responseInterceptorChain.length) {
        promise = promise.then(responseInterceptorChain.shift(), 		
      	responseInterceptorChain.shift());
    }
  
    return promise;

这里我们使用递归的方式处理同步的请求拦截器,因为我们必须每次调用拦截器后更新congfig,因此源码中采用递归的方式解决config更新的问题。同时,每次也是shift出数组的两个函数,用try-catch的方式处理使用拦截器的成功与失败,保证了两个函数一组出数组的统一性。后面用异步处理dispatchRequest和响应拦截器的部分跟之前基本类似,就不再赘述了,注意dispatchRequest本身是一个同步方法,但他返回一个异步对象,因此可以直接写在try里面,不需要用promise处理
我们可以看到拦截器加入的规则:由于请求拦截器用unshift添加,因此后添加的先调用;响应拦截器用push添加,因此先添加的先调用

之后有一个geturl的方法,用于返回完整的url,主要是要使用正则

// 获取完整url
Axios.prototype.getUri = function getUri(config) {
    config = mergeConfig(this.defaults, config);
    return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};

下面这部分,是axios为了简化我们的写法,使用工厂模式为我们批量创建的多个方法,这些方法本质上还是使用request进行处理,但利用合并配置的mergeConfig函数支持我们通过结构赋值的方式进行传参或多种格式进行传参,也就是我们平时最常用的版本
注意这里的foreach是until函数中自定义的foreach,不是JS自带的API

// 使用工厂模式:批量处理和添加多种方法,分为需要携带数据和不需要携带数据的
// 这里的作用相当于兼容了更多的写法格式,比如axios.get('url'),axios.post("url",data)等,本质上还是调用request方法
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
    /*eslint func-names:0*/
    Axios.prototype[method] = function(url, config) {
        // 支持解构赋值
        return this.request(mergeConfig(config || {}, {
            method: method,
            url: url,
            data: (config || {}).data //优先使用config中的参数,也可以不携带
        }));
    };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
    /*eslint func-names:0*/
    Axios.prototype[method] = function(url, data, config) {
        return this.request(mergeConfig(config || {}, {
            method: method,
            url: url,
            data: data //优先使用传进来的参数
        }));
    };
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
axios是一个基于Promise的HTTP客户端库,可以用于浏览器和Node.js环境中向服务器发送HTTP请求。它是一个功能强大且易于使用的库,可以与Vue.js等前端框架很好地配合使用。 vue-axios是一个适用于Vue.js的插件,它将axios集成到Vue.js中,使我们可以在Vue组件中更方便地使用axios发送HTTP请求。通过使用vue-axios插件,我们可以在Vue实例和Vue组件中直接使用this.$http来代替axios实例来发送请求,简化了我们的代码并提高了开发效率。 虽然我们也可以直接使用axios来发送请求,但使用vue-axios插件更符合Vue整体生态环境的设计原则。直接写原型链的方式可能会显得比较粗暴,并且不太推荐,除非是在底层实现的时候。因此,我们更推荐使用vue-axios插件的方式来结合Vue.js框架使用axios。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [axios和vue-axios](https://blog.csdn.net/qq_43654065/article/details/121288529)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Vue axios和vue-axios的关系及使用区别](https://blog.csdn.net/u014641168/article/details/126096526)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值