(Ajax)axios源碼簡析(二)——Axios類與攔截器

傳送門:ios

Axios.prototype.request

Axios類的構造函數:axios

// /lib/core/Axios.js

function Axios(instanceConfig) {

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

    // 攔截器
    this.interceptors = {
        // 請求攔截器
        request: new InterceptorManager(),
        // 響應攔截器
        response: new InterceptorManager()
    };
}

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

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

流程圖以下:promise

圖片描述

這裏尤爲應注意到請求鏈中請求攔截器的順序(handlers數組的倒序),在使用axios.interceptors.request.use時,要留意這一點。函數

Axios類與入口文件中的axios

另外,Axios類還有Axios.prototype.getAxios.prototype.postAxios.prototype.delete等方法,實際上都是修改了Axios.prototype.request參數config中的method屬性。post

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類,咱們再次回到入口文件:this

// /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()等請求方法的由來。url

參考

深刻淺出 axios 源碼spa

相關文章
相關標籤/搜索