原始碼分析axios(1)~原始碼分析、模擬axios的建立

一樂樂 發表於 2022-01-24
iOS

■ 檢視原始碼發現,起初axios【instance=bind(Axios.prototype.request, context);】是一個函式,

但後續【 utils.extend(instance, Axios.prototype, context);】又給它新增上了一些方法屬性。

axios 函式物件可以呼叫自身axios(config)傳送請求也可以呼叫屬性的方法axios.request(config)傳送請求的原理:(看學習axios必知必會(2)https://www.cnblogs.com/shan333/p/15836026.html)



一、原始碼分析axios的建立

1、檢視axios的例項物件new Axios(defaultConfig);

① 在類Axios 發現有配置物件攔截器物件屬性

② 在Axios.js 檔案中,還發現Axios的原型:Axios.prototype動態新增了屬性方法

requestgetUri

'delete', 'get', 'head', 'options',

'post', 'put', 'patch'

■ axios的建立的原始碼:

// 通過配置建立 axios 函式
var axios = createInstance(defaults);

function createInstance(defaultConfig) {
    //建立一個例項物件 context 可以呼叫 get  post put delete request
    var context = new Axios(defaultConfig);// context 不能當函式使用 
	……
}

//在 Axios 類內有屬性 配置物件和攔截器物件
function Axios(instanceConfig) {
    //例項物件上的 defaults 屬性為配置物件
    this.defaults = instanceConfig;
    //例項物件上有 interceptors 屬性用來設定請求和響應攔截器
    this.interceptors = {
        request: new InterceptorManager(),
        response: new InterceptorManager()
    };
}

// 在Axios.js 檔案中,還發現: 
Axios.prototype.request = function request(config) {
	……
}
Axios.prototype.getUri = function getUri(config) {
	……
}
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
    /*eslint func-names:0*/
    Axios.prototype[method] = function (url, config) {
	……
   }
}
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
    /*eslint func-names:0*/
    Axios.prototype[method] = function (url, data, config) {
	……
   }
}

2、接著是instance:

首先是通過方法bind將request方法賦值給instance(此時instance是一個函式變數);

然後,通過方法extends 將Axios.prototype 上的方法新增到 instance身上(此時instance是一個

“函式物件”變數);還將例項物件content的屬性(配置物件攔截器物件屬性)新增到instance身上

■ axios的建立的原始碼:

function createInstance(defaultConfig) {
//建立Axios的例項物件 context 
    var context = new Axios(defaultConfig);//context 是例項物件,不能當函式使用  
//將request 方法的 this 指向 context 並返回新函式 (instance 與 Axios.prototype.request 程式碼一致)
    var instance = bind(Axios.prototype.request, context); //instance是函式,可以用作函式使用
//將Axios的原型 Axios.prototype 的方法都新增到 instance 函式身上
   utils.extend(instance, Axios.prototype, context); //instance是"函式物件",可當函式使用,也可當物件使用
//將例項物件content的方法和屬性新增到 instance 函式身上
  utils.extend(instance, context);

  return instance;
}

■ bind函式:

//返回一個新的函式,並將新函式 this 繫結到一個物件身上
module.exports = function bind(fn, thisArg) {
    return function wrap() {
        var args = new Array(arguments.length);
        for (var i = 0; i < args.length; i++) {
            args[i] = arguments[i];
        }
        return fn.apply(thisArg, args);
    };
};

■ extend函式:

function extend(a, b, thisArg) {
    forEach(b, function assignValue(val, key) {
        if (thisArg && typeof val === 'function') {
            a[key] = bind(val, thisArg);
        } else {
            a[key] = val;
        }
    });
    return a;
}



二、模擬axios的建立

❀ 實現呼叫axios(config)自身傳送請求、呼叫axios物件的方法屬性 axios.request(config)傳送請求

<script>
    //建構函式
    function Axios(config) {
    //初始化
        this.defaults = config;//default預設屬性
        this.intercepters = {
            request: {},
            response: {}
        }
    }

    //為類的原型新增相關方法
    Axios.prototype.request = function (config) {
        console.log('傳送ajax請求,請求型別:' + config.method)
    }
    Axios.prototype.get = function (config) {
        return this.request({method: 'GET'})
    }
    Axios.prototype.post = function (config) {
        return this.request({method: 'POST'})
    }

    //宣告函式
    function createInstance(config) {
    //例項化一個物件
    var context = new Axios(config); //例項物件可以呼叫方法,例如 context.get() 但是不能直接當函式使用 context() ×
    var instance = Axios.prototype.request.bind(context);//instance 是一個函式,並且可以 instance({}),
    //但是因為bind返回的是一個函式(一個擁有了Axios.prototype.request() 方法的函式,而instance的引數就是Axios的例項),所以不能 instance.get()

    //將Axios.prototype 物件中的方法新增到instance函式中,讓instance擁有get、post、request等方法屬性
    Object.keys(Axios.prototype).forEach(key => {
        // console.log(key); //修改this指向context
        instance[key] = Axios.prototype[key].bind(context);
    })
//總結一下,到此instance自身即相當於Axios原型的request方法,
//然後又給instance的屬性新增了上了Axios原型的request、get、post方法屬性
//然後呼叫instance自身或instance的方法屬性時,修改了this指向context這個Axios例項物件

//為instance函式物件新增屬性 default 與 intercetors
        Object.keys(context).forEach(key => {
            instance[key] = context[key];
        })

     // console.dir(instance);
        return instance;
    }

    //測試一下axios的建立過程
    let axios = createInstance();
    //傳送Ajax請求
    axios({method: 'POST'});
    axios.post({});
</script>

相關文章