日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
手寫Axios核心原理

Axios是一個(gè)基于promise的HTTP庫(kù),它能夠自動(dòng)判斷當(dāng)前環(huán)境,自由切換在瀏覽器和 node.js環(huán)境中。如果是瀏覽器,就會(huì)基于XMLHttpRequests實(shí)現(xiàn);如果是node環(huán)境,就會(huì)基于node內(nèi)置核心http模塊實(shí)現(xiàn)。同時(shí),它還用promise處理了響應(yīng)結(jié)果,避免陷入回調(diào)地獄中去。

不僅如此,Axios還可以攔截請(qǐng)求和響應(yīng)、轉(zhuǎn)化請(qǐng)求數(shù)據(jù)和響應(yīng)數(shù)據(jù)、中斷請(qǐng)求、自動(dòng)轉(zhuǎn)換JSON數(shù)據(jù)、客戶端支持防御XSRF等。如此眾多好用的功能,快來(lái)一起看看它是如何實(shí)現(xiàn)的吧!

1.基本使用

axios基本使用方式主要有:

  • axios(config)
  • axios.method(url,data,config)
 
 
 
 
  1. // 發(fā)送 POST 請(qǐng)求
  2. axios({
  3.   method: 'post',
  4.   url: '/user/12345',
  5.   data: {
  6.     username: 'Web前端嚴(yán)選',
  7.     age: 2
  8.   }
  9. });
  10. // GET請(qǐng)求ID參數(shù)
  11. axios.get('/user?ID=12345')
  12.   .then(function (response) {
  13.     console.log(response);
  14.   })
  15.   .catch(function (error) {
  16.     console.log(error);
  17.   });

2.實(shí)現(xiàn)axios

從axios(config)的使用上可以看出導(dǎo)出的axios是一個(gè)方法,從axios.get()的使用上可以看出導(dǎo)出的axios原型上會(huì)有g(shù)et,post,put,delete等方法。

由分析可知,axios實(shí)際上是Axios類中的一個(gè)方法。我們可以先寫一個(gè)request方法來(lái)實(shí)現(xiàn)主要的請(qǐng)求功能,這樣就能使用axios(config)形式來(lái)調(diào)用了。

 
 
 
 
  1. class Axios{
  2.     constructor(){
  3.     }
  4.     request(config){
  5.         return new Promise((resolve) => {
  6.             const {url='',data={},method='get'} = config; //解構(gòu)傳參
  7.             const xhr = new XMLHttpRequest;     //創(chuàng)建請(qǐng)求對(duì)象
  8.             xhr.open(method,url,true); 
  9.             xhr.onreadystatechange = () => {
  10.                 if(xhr.readyState == 4 && xhr.status == 200){
  11.                     resolve(xhr.responseText);
  12.                     //異步請(qǐng)求返回后將Promise轉(zhuǎn)為成功態(tài)并將結(jié)果導(dǎo)出
  13.                 }
  14.             }
  15.             xhr.send(JSON.stringfy(data));
  16.         })
  17.     }
  18. }
  19. function CreateAxiosFn(){
  20.     let axios = new Axios;
  21.     let req = axios.request.bind(axios);
  22.     return req;
  23. }
  24. let axios = CreateAxiosFn();

然后搭建一個(gè)簡(jiǎn)易服務(wù)端代碼,以測(cè)試請(qǐng)求的效果:

 
 
 
 
  1. const express = require('express')
  2. let app = express();
  3. app.all('*', function (req, res, next) {
  4.     res.header('Access-Control-Allow-Origin', '*');
  5.     res.header('Access-Control-Allow-Headers', 'Content-Type');
  6.     res.header('Access-Control-Allow-Methods', '*');
  7.     res.header('Content-Type', 'application/json;charset=utf-8');
  8.     next();
  9. });
  10. app.get('/getInfo', function(request, response){
  11.     let data = {
  12.         'username':'前端嚴(yán)選',
  13.         'age':'2'
  14.     };
  15.     response.json(data);
  16. });
  17. app.listen(3000, function(){
  18.     console.log("服務(wù)器啟動(dòng)");
  19. });

啟動(dòng)服務(wù)后,在頁(yè)面中測(cè)試請(qǐng)求是否成功:

 
 
 
 
  1. 點(diǎn)擊

點(diǎn)擊按鈕后,可以看到請(qǐng)求成功并獲取到數(shù)據(jù)。

3.原型上的方法

接下來(lái)實(shí)現(xiàn)以axios.method()形式的方法。

通過(guò)axios.get(),axios.post(),axios.put()等方法可以看出它們都是Axios.prototype上的方法,這些方法調(diào)用內(nèi)部的request方法即可:

 
 
 
 
  1. const methodsArr = ['get','post','put','delete','head','options','patch','head'];
  2. methodsArr.forEach(method => {
  3.     Axios.prototype[method] = function(){
  4.         return this.request({
  5.             method: method,
  6.             ...arguments[0]
  7.         })
  8.     }
  9. })

arguments的第一個(gè)參數(shù)包含url,data等信息,直接解構(gòu)它的第一個(gè)元素即可

還需要實(shí)現(xiàn)一個(gè)工具方法,用來(lái)將b方法屬性混入到a中去:

 
 
 
 
  1. const utils = {
  2.     extend(a,b,context){
  3.         for(let key in b){
  4.             if(b.hasOwnProperty(key)){
  5.                 if(typeof b[key] == 'function'){
  6.                     a[key] = b[key].bind(context);
  7.                 }else{
  8.                     a[key] = b[key]
  9.                 }
  10.             }
  11.         }
  12.     }
  13. }

最終導(dǎo)出axios的request方法,使之擁有g(shù)et,post等方法

 
 
 
 
  1. function CreateAxiosFn(){
  2.     let axios = new Axios;
  3.     let req = axios.request.bind(axios);
  4.     //新增如下代碼
  5.     utils.extend(req, Axios.prototype, axios)
  6.     return req;
  7. }

再來(lái)測(cè)試一下post的請(qǐng)求:

 
 
 
 
  1. axios.post({
  2.     url: 'http://localhost:3000/postTest',
  3.     data: {
  4.         a: 1,
  5.         b: 2
  6.     }
  7. }).then(res => {
  8.     console.log(res);
  9. })

可以看到正確返回結(jié)果了。

4.攔截器

先來(lái)看看攔截器的使用:

 
 
 
 
  1. // 請(qǐng)求攔截
  2. axios.interceptors.request.use(function (config) {
  3.     // 在發(fā)送請(qǐng)求之前
  4.     return config;
  5.   }, function (error) {
  6.     // 請(qǐng)求錯(cuò)誤處理
  7.     return Promise.reject(error);
  8.   });
  9. // 響應(yīng)攔截
  10. axios.interceptors.response.use(function (response) {
  11.     // 響應(yīng)數(shù)據(jù)處理
  12.     return response;
  13.   }, function (error) {
  14.     // 響應(yīng)錯(cuò)誤處理
  15.     return Promise.reject(error);
  16.   });

攔截器,顧名思義就是在請(qǐng)求之前和響應(yīng)之前,對(duì)真正要執(zhí)行的操作數(shù)據(jù)攔截住進(jìn)行一些處理。

那么如何實(shí)現(xiàn)呢,首先攔截器也是一個(gè)類,用于管理響應(yīng)和請(qǐng)求。

 
 
 
 
  1. class InterceptorsManage{
  2.     constructor(){
  3.         this.handlers = [];
  4.     }
  5.     use(onFulField,onRejected){
  6.         //將成功的回調(diào)和失敗的回調(diào)都存放到隊(duì)列中
  7.         this.handlers.push({
  8.             onFulField,
  9.             onRejected
  10.         })
  11.     }
  12. }

axios.interceptors.response.use和axios.interceptors.request.use來(lái)定義請(qǐng)求和響應(yīng)的攔截方法。

這說(shuō)明axios上有響應(yīng)攔截器和請(qǐng)求攔截器,那么如何在axios上實(shí)現(xiàn)呢:

 
 
 
 
  1. class Axios{
  2.     constructor(){
  3.         this.interceptors = {
  4.             request: new InterceptorsManage,
  5.             response: new InterceptorsManage
  6.         }
  7.     }
  8.     //....
  9. }

在Axios的構(gòu)造函數(shù)中新增interceptors屬性,然后定義request和response屬性用于處理請(qǐng)求和響應(yīng)。

執(zhí)行use方法時(shí),會(huì)把傳入的回調(diào)函數(shù)放到handlers數(shù)組中。

這時(shí)再回看使用方式,axios.interceptors.request.use方法是綁在axios實(shí)例上的,因此同樣需要把Axios上的屬性和方法轉(zhuǎn)移到request上,將interceptors對(duì)象掛載到request方法上。

 
 
 
 
  1. function CreateAxiosFn() {
  2.   let axios = new Axios();
  3.   let req = axios.request.bind(axios);
  4.   utils.extend(req, Axios.prototype, axios)
  5.   //新增如下代碼
  6.   utils.extend(req, axios)
  7.   return req;
  8. }

但是現(xiàn)在request不僅要執(zhí)行請(qǐng)求的發(fā)送,還要執(zhí)行攔截器中handler的回調(diào)函數(shù),因此還需要把request方法進(jìn)行一下改造:

 
 
 
 
  1. request(config){
  2.     //攔截器和請(qǐng)求的隊(duì)列
  3.     let chain = [this.sendAjax.bind(this),undefined];
  4.  //請(qǐng)求的攔截
  5.     this.interceptors.request.handlers.forEach(interceptor => {
  6.         chain.unshift(interceptor.onFulField,interceptor.onRejected);
  7.     })
  8.  //響應(yīng)的攔截
  9.     this.interceptors.response.handlers.forEach(interceptor => {
  10.         chain.push(interceptor.onFulField,interceptor.onRejected)
  11.     })
  12.     let promise = Promise.resolve(config);
  13.     while(chain.length > 0){
  14.         //從頭部開始依次執(zhí)行請(qǐng)求的攔截、真正的請(qǐng)求、響應(yīng)的攔截
  15.         promise = promise.then(chain.shift(),chain.shift());
  16.     }
  17.     return promise;
  18. }
  19. sendAjax(config){
  20.     return new Promise((resolve) => {
  21.         const {url='',method='get',data={}} = config;
  22.         const xhr = new XMLHttpRequest();
  23.         xhr.open(method,url,true);
  24.         xhr.onreadystatechange = () => {
  25.             if(xhr.readyState == 4 && xhr.status == 200){
  26.                 resolve(xhr.responseText)
  27.             }
  28.         }
  29.         xhr.send(JSON.stringify(data));
  30.     })
  31. }

最后執(zhí)行chain的時(shí)候是這個(gè)樣子的:

 
 
 
 
  1. chain = [
  2.     //請(qǐng)求之前成功的回調(diào)和失敗的回調(diào)
  3.     function (config) {
  4.         return config;
  5.     }, 
  6.     function (error) {
  7.         return Promise.reject(error);
  8.     }
  9.  //真正的請(qǐng)求執(zhí)行
  10.     this.sendAjax.bind(this), 
  11.     undefined,
  12.  //請(qǐng)求之后響應(yīng)的成功回調(diào)和失敗回調(diào)
  13.     function (response) {
  14.         return response;
  15.     }, 
  16.     function (error) {
  17.         return Promise.reject(error);
  18.     }
  19. ]

請(qǐng)求之前,promise執(zhí)行為:

 
 
 
 
  1. promise.then(
  2.  function (config) {
  3.         return config;
  4.     }, 
  5.     function (error) {
  6.         return Promise.reject(error);
  7.     }
  8. )

請(qǐng)求時(shí),執(zhí)行為:

 
 
 
 
  1. promise.then(
  2.  this.sendAjax.bind(this), 
  3.     undefined,
  4. )

響應(yīng)后,執(zhí)行為:

 
 
 
 
  1. promise.then(
  2.  function (response) {
  3.         return response;
  4.     }, 
  5.     function (error) {
  6.         return Promise.reject(error);
  7.     }
  8. )

這時(shí)我們測(cè)試一下攔截器的使用:

 
 
 
 
  1. function getMsg(){
  2.     axios.interceptors.request.use((config) => {
  3.         console.log('請(qǐng)求攔截:',config);
  4.         return config;
  5.     },err => {
  6.         return Promise.reject(err)
  7.     })
  8.     axios.interceptors.response.use(response => {
  9.         response = {
  10.             message: '響應(yīng)數(shù)據(jù)替換',
  11.             data: response
  12.         }
  13.         return response
  14.     },err => {
  15.         console.log(err,'響應(yīng)錯(cuò)誤')
  16.         return Promise.reject(err)
  17.     })
  18.     axios.get({
  19.         url: 'http://localhost:3000/getTest',
  20.     }).then(res => {
  21.         console.log(res);
  22.     })
  23. }

可以在控制臺(tái)中看到攔截處理的打印輸出,證明攔截成功!

5.總結(jié)

Axios天然支持Promise的性能讓其方便對(duì)異步進(jìn)行處理,同時(shí)又利用了Promise對(duì)請(qǐng)求進(jìn)行了攔截,使得用戶可以在請(qǐng)求過(guò)程中添加更多的功能,對(duì)請(qǐng)求的中斷能自如操作。它的思想既清新樸實(shí)又不落入俗套,具有很好的借鑒意義。

看完這篇文章,你了解了Axios的核心原理了嗎?

本文轉(zhuǎn)載自微信公眾號(hào)「Web前端嚴(yán)選」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Web前端嚴(yán)選公眾號(hào)。


網(wǎng)站名稱:手寫Axios核心原理
網(wǎng)站路徑:http://m.5511xx.com/article/cogspso.html