從原始碼分析Axios

Annter發表於2020-03-24

API 解析

Axios中,可以使用多種方法請求資料,這讓Axios變得及其簡單易用。

它既可以傳字串進 axios 中,也可以使用物件傳入。

使用 Axios 請求

axios({
  url: 'localhost:3000',
  data: {
    firstName: 1,
    lastName: 2
  }
}).then(res => {
  console.log(res);
});

// 這是使用字串的請求方式

axios('localhost:3000').then(res => {
  console.log(res);
});
複製程式碼

使用 HTTP 的請求方法

axios.get('localhost:3000').then(res => {
  console.log(res);
});
複製程式碼

兩種使用方法都可以使Axios正常執行,而它用的是例項化物件,然後修改上下文的方法,可以讓它正常運轉。

建立 Axios 物件

axios的入口檔案處,axios用到了許多方法,包括例項的繫結,以及屬性和方法的繼承。

繫結函式

function bind(fn, thisArg) {
  // 閉包函式,返回已經修改好的作用域
  return function wrap() {
    var args = new Array(arguments.length);

    for (var i = 0; i < args.length; i++) {
      // 將引數拷貝到args上
      args[i] = arguments[i];
    }
    // 執行一次函式
    return fn.apply(thisArg, args);
  };
}
複製程式碼

繼承屬性

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

new __Axios__  {
    constructor(config) {
        this.config = config;
    }
    request(config = this.config){
        return config ? config : 'config'
    }
}

// 例項化物件
const context = new __Axios__();

// 使用bind方法將原型鏈上的`request`指向是__Axios__上下文

const instance =  bind(__Axios__.prototype.request,context);

// 將Axios的所有方法合併instance上去
extend(instance,Axios.prototype,context);
// 將Axios的所有屬性都合併到instance上去
extend(instance,context);
複製程式碼

這樣,Axios既可以用get,post等方法獲取資料,又能使用Axios()獲取資料。


攔截器

首先,寫一個攔截管理器的類。

class InterceptorManager {
  constructor() {
    this.handlers = [];
  }
  // 接收攔截或請求的方法
  use(fulfilled, rejected) {
    this.handlers.push({ fulfilled, rejected });
  }
  // 清除掉接收或請求的方法
  reject(id) {
    this.handlers[id] && this.handlers[id] = null;
  }
  // 將所有攔截器遍歷一次
  forEach(fn) {
    this.handlers.forEach(item => {
      item && fn(item);
    });
  }
}
複製程式碼

例項化攔截器

接著,我們將攔截器例項化,類似下面的樣子:

class Axios {
  constructor() {
    this.interceptor = {
      request: new InterceptorManager(),
      response: new InterceptorManager()
    };
  }
}
複製程式碼

合成攔截器鏈

既然有了攔截器,那麼我們可以直接地傳送請求了。

思路是:


  1. ResquestInterceptorFulfilled ---> ResquestInterceptorReject // 請求攔截器鏈

  2. dispatchRequest ----> undefined // 分發請求

  3. ResponseInterceptorFulfilled ---> ResponseInterceptorReject // 響應攔截器鏈


class Axios {
  constructor() {
    this.interceptor = {
      request: new InterceptorManager(),
      response: new InterceptorManager()
    };
  }
  request() {
    // dispatchRequest是指,分發的請求
    const china = [dispathRequest, undefined];
    const promise = Promise.resolve(config);

    // 將請求攔截器推入鏈中
    this.interceptor.request.forEach(interceptor => {
      china.unshift(interceptor.fulfilled, interceptor.rejected);
    });
    // 將相應攔截器推入鏈中
    this.interceptor.response.forEach(interceptor => {
      china.push(interceptor.fulfilled, interceptor.rejected);
    });

    while (china.length) {
      // 逐個遍歷,然後執行
      promise = promise.then(chain.shift(), chain.shift());
    }
    return promise;
  }
}
複製程式碼

相關文章