5

传送门:

Axios.prototype.request

Axios类的构造函数:

// /lib/core/Axios.js

function Axios(instanceConfig) {

    // 默认配置
    this.defaults = instanceConfig;

    // 拦截器
    this.interceptors = {
        // 请求拦截器
        request: new InterceptorManager(),
        // 响应拦截器
        response: new InterceptorManager()
    };
}

Axios.prototype.request方法是Axios类原型方法中的重中之重。

Axios.prototype.request = function request(config) {
    // 用于API中的 axios(config) 或 axios(url[, config])
    if (typeof config === 'string') {
        config = arguments[1] || {};
        config.url = arguments[0];
    } else {
        config = config || {};
    }

    // 将传入的配置与默认配置合并
    config = mergeConfig(this.defaults, config);
    
    // 设置请求方法
    config.method = config.method ? config.method.toLowerCase() : 'get';
    
    
    // 请求拦截
    // 发送请求
    // 响应拦截
}

在分析Axios.prototype.request中拦截器与请求相关代码之前,要先分析拦截器的原理

拦截器的实现

// /lib/core/InterceptorManager.js

function InterceptorManager() {
    this.handlers = [];
}


// 将一个拦截器添加到handlers数组
// 对应API中的 axios.interceptors.request.use 与 axios.interceptors.resopnse.use
// 返回拦截器的ID
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
    this.handlers.push({
        fulfilled: fulfilled,
        rejected: rejected
    });
    return this.handlers.length - 1;
};


// 移除指定ID的拦截器
InterceptorManager.prototype.eject = function eject(id) {
    if (this.handlers[id]) {
        this.handlers[id] = null;
    }
};


// 遍历执行handlers数组中的拦截器,跳过被eject成null的项
InterceptorManager.prototype.forEach = function forEach(fn) {
    utils.forEach(this.handlers, function forEachHandler(h) {
        if (h !== null) {
            fn(h);
        }
    });
};


module.exports = InterceptorManager;

回到 Axios.prototype.request

Axios.prototype.request = function request(config) {
    /* 配置相关代码 */
    
    // 请求链 chain
    // 首先加入发送请求的方法和 undefined
    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);
    });

    // 遍历请求链,形成 promise 链
    while (chain.length) {
        promise = promise.then(chain.shift(), chain.shift());
    }

    // 返回promise链
    return promise;
}

流程图如下:

图片描述

这里尤其应注意到请求链中请求拦截器的顺序(handlers数组的倒序),在使用axios.interceptors.request.use时,要留意这一点。

Axios类与入口文件中的axios

另外,Axios类还有Axios.prototype.getAxios.prototype.postAxios.prototype.delete等方法,实际上都是修改了Axios.prototype.request参数config中的method属性。

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
        }));
    };
});

分析完Axios类,我们再次回到入口文件:

// /lib/axios.js

function createInstance(defaultConfig) {

    // 创建一个Axios类的实例,得到一个上下文环境
    // 包含defaults配置与拦截器(详见/lib/core/Axios.js)
    var context = new Axios(defaultConfig);

    // instance是一个函数(request请求方法)
    // this绑定到context上下文
    var instance = bind(Axios.prototype.request, context);

    // 将Axios.prototype的各方法绑定到instance上
    // 其中this作用域为context上下文
    utils.extend(instance, Axios.prototype, context);

    // 将context中的属性(defaults与拦截器)绑定到instance实例中
    utils.extend(instance, context);

    return instance;
}


var axios = createInstance(defaults);

/* ... */

modules.exports = axios;

现在我们就可以在Axios类中找到,axios中的defaultsinterceptors属性,以及axios()axios.get()axios.post()等请求方法的由来。

参考

深入浅出 axios 源码


kimi013
247 声望10 粉丝