最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

axios攔截器工作方式及原理源碼解析

 更新時(shí)間:2023年02月10日 17:00:11   作者:鋒利的綿羊  
這篇文章主要為大家介紹了axios攔截器工作原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

axios 攔截器的配置方式

本文所用 axios 版本號為:1.3.2。

axios 中有兩種攔截器:

  • axios.interceptors.request.use(onFulfilled, onRejected, options):配置請求攔截器。
    • onFulfilled 方法在發(fā)送請求前執(zhí)行,接收 config 對象,返回一個(gè)新的 config 對象,可在此方法內(nèi)修改 config 對象。
    • onRejected 方法在 onFulfilled 執(zhí)行錯(cuò)誤后執(zhí)行,接收 onFulfilled 執(zhí)行后的錯(cuò)誤對象。
    • options 配置參數(shù)
      • synchronous:控制請求攔截器是否為異步執(zhí)行,默認(rèn)為 true,每個(gè)攔截器都可以單獨(dú)設(shè)置,只要有一個(gè)設(shè)置為 false 則為同步執(zhí)行,否則為異步執(zhí)行。
      • runWhen:一個(gè)方法,接收 config 對象作為參數(shù),在每次調(diào)用設(shè)定的攔截器方法前調(diào)用,返回結(jié)果為 true 時(shí)執(zhí)行攔截器方法。
  • axios.interceptors.response.use(onFulfilled, onRejected, options):配置響應(yīng)攔截器。
    • onFulfilled 方法在返回響應(yīng)結(jié)果前調(diào)用,接收響應(yīng)對象,返回一個(gè)新的響應(yīng)對象,可在此方法內(nèi)修改響應(yīng)對象。
    • onRejected 與 options 同 axios.interceptors.request.use。
axios.interceptors.request.use(
  function (config) {
    return config;
  },
  function (error) {
    return Promise.reject(error);
  }
);
axios.interceptors.response.use(
  function (response) {
    return response;
  },
  function (error) {
    return Promise.reject(error);
  }
);

可以添加多個(gè)攔截器:

axios.interceptors.request.use(
  function (config) {
    throw new Error("999");
    return config;
  },
  function (error) {
    console.log(1, error);
    return Promise.reject(error);
  }
);
axios.interceptors.request.use(
  function (config) {
    throw new Error("888");
    return config;
  },
  function (error) {
    console.log(2, error);
    return Promise.reject(error);
  }
);
axios.interceptors.response.use(
  function (response) {
    return response;
  },
  function (error) {
    console.log(3, error);
    return Promise.reject(error);
  }
);
axios.interceptors.response.use(
  function (response) {
    return response;
  },
  function (error) {
    console.log(4, error);
    return Promise.reject(error);
  }
);
axios.get("https://www.baidwwu.com").catch((error) => {
  console.log(4, error);
});
// 2  Error: 888
// 1  Error: 888
// 3  Error: 888
// 4  Error: 888
// 5  Error: 888

先執(zhí)行請求攔截器,后執(zhí)行響應(yīng)攔截器。

  • 設(shè)置多個(gè)請求攔截器的情況:后添加的攔截器先執(zhí)行。
  • 設(shè)置多個(gè)響應(yīng)攔截器的情況:按照設(shè)置順序執(zhí)行。

use() 方法的定義

先來看 use() 方法相關(guān)代碼:

this.interceptors = {
  request: new InterceptorManager$1(),
  response: new InterceptorManager$1(),
};
var InterceptorManager = (function () {
  function InterceptorManager() {
    this.handlers = [];
  }
  // ...
  // _createClass 方法可以給對象的原型上添加屬性
  _createClass(InterceptorManager, [
    {
      key: "use",
      value: function use(fulfilled, rejected, options) {
        this.handlers.push({
          fulfilled: fulfilled,
          rejected: rejected,
          synchronous: options ? options.synchronous : false,
          runWhen: options ? options.runWhen : null,
        });
        return this.handlers.length - 1;
      },
    },
    {
      key: "forEach",
      value: function forEach(fn) {
        utils.forEach(this.handlers, function forEachHandler(h) {
          if (h !== null) {
            fn(h);
          }
        });
      },
    },
    // ...
  ]);
  return InterceptorManager;
})();
var InterceptorManager$1 = InterceptorManager;

可以看到 interceptors.requestinterceptors.response 各自指向 InterceptorManager 的實(shí)例。

InterceptorManager 原型上設(shè)置了 use() 方法,執(zhí)行后給待執(zhí)行隊(duì)列 this.handlers 中添加一條數(shù)據(jù)。

這里利用了 this 的特性來區(qū)分作用域:誰調(diào)用 use() 方法就給誰的 handlers 中添加數(shù)據(jù)。在調(diào)用 use() 方法之后,已經(jīng)將回調(diào)函數(shù)按順序添加到了 handlers 數(shù)組中。

forEach() 方法用來遍歷當(dāng)前作用域 handlers 中不為 null 的元素,在執(zhí)行攔截器時(shí)有用到,詳情見下文。

攔截器如何執(zhí)行

攔截器是在調(diào)用了 request() 方法前后執(zhí)行的,先看相關(guān)源碼:

var Axios = (function () {
  _createClass(Axios, [
    {
      key: "request",
      value: function request(configOrUrl, config) {
        // ...
        // 初始化請求攔截器
        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
          );
        });
        // 初始化響應(yīng)攔截器
        var responseInterceptorChain = [];
        this.interceptors.response.forEach(function pushResponseInterceptors(
          interceptor
        ) {
          responseInterceptorChain.push(
            interceptor.fulfilled,
            interceptor.rejected
          );
        });
        var promise;
        var i = 0;
        var len;
        // 請求攔截器同步執(zhí)行模式
        if (!synchronousRequestInterceptors) {
          var chain = [dispatchRequest.bind(this), undefined];
          chain.unshift.apply(chain, requestInterceptorChain);
          chain.push.apply(chain, responseInterceptorChain);
          len = chain.length;
          promise = Promise.resolve(config);
          console.log(11, chain);
          while (i < len) {
            promise = promise.then(chain[i++]).catch(chain[i++]);
          }
          return promise;
        }
        // 請求攔截器異步執(zhí)行模式
        len = requestInterceptorChain.length;
        var newConfig = config;
        i = 0;
        while (i < len) {
          var onFulfilled = requestInterceptorChain[i++];
          var onRejected = requestInterceptorChain[i++];
          try {
            newConfig = onFulfilled(newConfig);
          } catch (error) {
            onRejected.call(this, error);
            break;
          }
        }
        try {
          promise = dispatchRequest.call(this, newConfig);
        } catch (error) {
          return Promise.reject(error);
        }
        i = 0;
        len = responseInterceptorChain.length;
        while (i < len) {
          promise = promise.then(
            responseInterceptorChain[i++],
            responseInterceptorChain[i++]
          );
        }
        return promise;
      },
    },
  ]);
})();

上面是相關(guān)的全部代碼,下面進(jìn)行分解。

攔截器回調(diào)方法的添加順序

var requestInterceptorChain = []; // 請求攔截器執(zhí)行鏈
// 是否同步執(zhí)行請求攔截器,每個(gè)攔截器都可以單獨(dú)設(shè)置,但是只有所有攔截器都設(shè)置為true才為true
var synchronousRequestInterceptors = true;
this.interceptors.request.forEach(function unshiftRequestInterceptors(
  interceptor
) {
  // 傳入 runWhen() 方法時(shí),如果 runWhen() 方法返回值為 false 則忽略這個(gè)請求攔截器
  if (
    typeof interceptor.runWhen === "function" &&
    interceptor.runWhen(config) === false
  ) {
    return;
  }
  // 是否同步執(zhí)行
  synchronousRequestInterceptors =
    synchronousRequestInterceptors && interceptor.synchronous;
  // 用 unshift() 添加數(shù)據(jù),后設(shè)置的攔截器在前
  requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
var responseInterceptorChain = []; // 響應(yīng)攔截器執(zhí)行鏈
this.interceptors.response.forEach(function pushResponseInterceptors(
  interceptor
) {
  // 響應(yīng)攔截器按順序加在后面
  responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});

interceptors.request.forEach() 的定義在上文提到過,封裝為遍歷自身的 handlers 數(shù)組,與數(shù)組的 forEach 類似,只是過濾了值為 null 的元素。

runWhen() 方法接收 config,返回 boolean,控制是否將當(dāng)前攔截器的回調(diào)函數(shù)添加到執(zhí)行鏈中。

請求攔截器用 unshift() 方法添加,所以后設(shè)置的先執(zhí)行,響應(yīng)攔截器用 push() 方法添加,所以按照設(shè)置順序執(zhí)行。

只要有一個(gè)攔截器的 synchronous 設(shè)置為 false,則 synchronousRequestInterceptors 的值為 false

同步執(zhí)行請求攔截器(順序執(zhí)行)

synchronousRequestInterceptorsfalse 時(shí)為同步執(zhí)行,相關(guān)邏輯如下:

var promise;
var i = 0;
var len;
if (!synchronousRequestInterceptors) {
  var chain = [dispatchRequest.bind(this), undefined]; // 執(zhí)行鏈
  chain.unshift.apply(chain, requestInterceptorChain); // 將請求攔截器添加到請求之前
  chain.push.apply(chain, responseInterceptorChain); // 將響應(yīng)攔截器添加到響應(yīng)之后
  len = chain.length;
  promise = Promise.resolve(config);
  while (i < len) {
    promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包裝執(zhí)行鏈
  }
  return promise;
}

dispatchRequest() 是發(fā)送請求的方法。

同步執(zhí)行模式下,會(huì)將執(zhí)行鏈中的所有方法用 Promise 進(jìn)行封裝,前一個(gè)方法執(zhí)行完畢后將其返回值作為參數(shù)傳遞給下一個(gè)方法。

這里的 chain 其實(shí)就是所有攔截器方法與請求方法合并而成的執(zhí)行鏈,等價(jià)于: [...requestInterceptorChain, dispatchRequest.bind(this), ...responseInterceptorChain]。

從一個(gè)例子來看 chain 的成員:

axios.interceptors.request.use(onFulfilled1, onRejected1);
axios.interceptors.request.use(onFulfilled2, onRejected2);
axios.interceptors.response.use(onFulfilled3, onRejected3);
axios.interceptors.response.use(onFulfilled4, onRejected4);
axios.get();
// chain: [onFulfilled2, onRejected2, onFulfilled1, onRejected1, dispatchRequest, onFulfilled3, onRejected3, onFulfilled4, onRejected4]

在構(gòu)建 Promise 鏈的時(shí)候,一次遍歷中取了兩個(gè)方法傳遞給 then():

promise = Promise.resolve(config);
while (i < len) {
  promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包裝執(zhí)行鏈
}
return promise;

異步執(zhí)行請求攔截器(同時(shí)執(zhí)行)

var promise;
var i = 0;
var len = requestInterceptorChain.length; // 請求執(zhí)行鏈的長度
var newConfig = config;
i = 0;
// 執(zhí)行請求攔截器回調(diào)
while (i < len) {
  var onFulfilled = requestInterceptorChain[i++]; // use() 的第一個(gè)參數(shù)
  var onRejected = requestInterceptorChain[i++]; // use() 的第二個(gè)參數(shù)
  // 執(zhí)行成功后繼續(xù)執(zhí)行,執(zhí)行失敗后停止執(zhí)行。
  try {
    newConfig = onFulfilled(newConfig);
  } catch (error) {
    onRejected.call(this, error);
    break;
  }
}
// 執(zhí)行發(fā)送請求方法
try {
  promise = dispatchRequest.call(this, newConfig);
} catch (error) {
  return Promise.reject(error); // 執(zhí)行失敗后退出
}
// 執(zhí)行響應(yīng)攔截器回調(diào)
i = 0;
len = responseInterceptorChain.length;
while (i < len) {
  promise = promise.then(
    responseInterceptorChain[i++], // use() 的第一個(gè)參數(shù)
    responseInterceptorChain[i++] // use() 的第二個(gè)參數(shù)
  );
}
return promise;

dispatchRequest() 是發(fā)送請求的方法。

while 循環(huán)遍歷所有的請求攔截器并調(diào)用,將執(zhí)行語句包裹在 try-catch 語句中,只要有一個(gè)請求攔截器異常就停止循環(huán)。

可以看到在異步模式下,請求攔截器為異步執(zhí)行,但是不影響發(fā)送請求,而響應(yīng)攔截器還是在請求響應(yīng)后同步執(zhí)行。

Q&A

攔截器是如何工作的

調(diào)用 .request.use().response.use() 方法時(shí),將傳入的攔截器回調(diào)方法分別存入 請求攔截器回調(diào)數(shù)組響應(yīng)攔截器回調(diào)數(shù)組。

在調(diào)用 .get() 等方法時(shí),將 請求攔截器回調(diào)數(shù)組響應(yīng)攔截器回調(diào)數(shù)組 與發(fā)送請求的方法拼接成一個(gè)完整的執(zhí)行鏈,按照同步或異步的模式調(diào)用執(zhí)行鏈中的方法。

響應(yīng)攔截器總是在返回響應(yīng)結(jié)果后按順序執(zhí)行。

請求攔截器根據(jù) synchronous 配置不同,行為有所不同:

  • 異步執(zhí)行請求攔截器模式。
  • 沒有設(shè)置 synchronous 時(shí)默認(rèn)為異步執(zhí)行請求攔截器模式,即遍歷執(zhí)行所有的請求攔截器一參回調(diào),執(zhí)行報(bào)錯(cuò)后停止遍歷,并執(zhí)行該攔截器的二參回調(diào)。
  • 同步執(zhí)行請求攔截器模式。
  • 設(shè)置 synchronousfalse 時(shí)為同步執(zhí)行請求攔截器模式,將執(zhí)行鏈包裝成一個(gè) Promise 鏈順序執(zhí)行。

攔截器的執(zhí)行順序

先執(zhí)行請求攔截器,后執(zhí)行響應(yīng)攔截器。

  • 設(shè)置多個(gè)請求攔截器的情況:后添加的攔截器先執(zhí)行。
  • 設(shè)置多個(gè)響應(yīng)攔截器的情況:按照設(shè)置順序執(zhí)行。

同步&異步

計(jì)算機(jī)中的同步,指的是現(xiàn)實(shí)中的一步一步(同一時(shí)間只能干一件事),異步指的是同時(shí)進(jìn)行(同一時(shí)間能干多件事)。

參考 npm axios

以上就是axios攔截器工作方式及原理源碼解析的詳細(xì)內(nèi)容,更多關(guān)于axios攔截器工作原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue路由--網(wǎng)站導(dǎo)航功能詳解

    vue路由--網(wǎng)站導(dǎo)航功能詳解

    這篇文章主要介紹了vue路由--網(wǎng)站導(dǎo)航功能詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • vue使用smooth-signature實(shí)現(xiàn)移動(dòng)端橫豎屏電子簽字

    vue使用smooth-signature實(shí)現(xiàn)移動(dòng)端橫豎屏電子簽字

    這篇文章主要為大家介紹了vue使用smooth-signature實(shí)現(xiàn)移動(dòng)端橫豎屏電子簽字示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 詳解如何在Vue3中與后端API進(jìn)行交互

    詳解如何在Vue3中與后端API進(jìn)行交互

    在現(xiàn)代的 web 應(yīng)用中,與后端 API 的交互成為了不可或缺的一部分,特別是在 Vue 3 這樣的前端框架中,如何高效地進(jìn)行數(shù)據(jù)請求,處理響應(yīng),本文將帶您深入了解在 Vue 3 中如何與后端 API 進(jìn)行交互,并通過示例代碼幫助您快速上手,需要的朋友可以參考下
    2025-01-01
  • vue mintui-Loadmore結(jié)合實(shí)現(xiàn)下拉刷新和上拉加載示例

    vue mintui-Loadmore結(jié)合實(shí)現(xiàn)下拉刷新和上拉加載示例

    本篇文章主要介紹了vue mintui-Loadmore結(jié)合實(shí)現(xiàn)下拉刷新和上拉加載示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • vue項(xiàng)目npm?run?build打包dist文件及打包后空白解決辦法

    vue項(xiàng)目npm?run?build打包dist文件及打包后空白解決辦法

    npm run build 這個(gè)命令會(huì)執(zhí)行Vue CLI中預(yù)定義的打包配置,并將打包后的文件存放在"dist"文件夾中,這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目npm?run?build打包dist文件及打包后空白的解決辦法,需要的朋友可以參考下
    2023-10-10
  • vue3中路由寫法與傳參方式的超詳細(xì)指南

    vue3中路由寫法與傳參方式的超詳細(xì)指南

    Vue?Router是Vue.js官方的路由管理器,它主要用于單頁面應(yīng)用程序中,幫助解決頁面導(dǎo)航、組件復(fù)用等問題,下面我們就來看看vue3中路由寫法與傳參方式吧
    2024-11-11
  • Vue3中無法為el-tree-select設(shè)置反選問題解析

    Vue3中無法為el-tree-select設(shè)置反選問題解析

    這篇文章主要介紹了Vue3中無法為el-tree-select設(shè)置反選問題分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • vue router的基本使用和配置教程

    vue router的基本使用和配置教程

    這篇文章主要介紹了vue-router的基本使用和配置教程,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧,需要的朋友可以參考下
    2018-11-11
  • vue3 Element-Plus el-backtop無效問題及解決

    vue3 Element-Plus el-backtop無效問題及解決

    這篇文章主要介紹了vue3 Element-Plus el-backtop無效問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue?的全選組件封裝你知道多少

    vue?的全選組件封裝你知道多少

    這篇文章主要為大家詳細(xì)介紹了vue的全選組件封裝,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02

最新評論

普安县| 霍邱县| 杨浦区| 荔波县| 忻州市| 武隆县| 寿阳县| 嘉义市| 九寨沟县| 黔江区| 平和县| 贵州省| 松江区| 红安县| 洪江市| 肥西县| 庆城县| 长宁区| 玉龙| 漳平市| 山阳县| 西乡县| 内乡县| 黄陵县| 襄垣县| 遂昌县| 武穴市| 阳朔县| 皋兰县| 体育| 龙门县| 香港| 大新县| 长沙市| 双牌县| 克拉玛依市| 博兴县| 鄂伦春自治旗| 六盘水市| 新安县| 元江|