手写Axios核心原理,再也不怕面试官问我Axios原理

开发 前端
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。axios可以用在浏览器和 node.js 中是因为,它会自动判断当前环境是什么,如果是浏览器,就会基于XMLHttpRequests实现axios。

 手写axios核心原理

  • 手写axios核心原理,再也不怕面试官问我axios原理
  • 手写axios核心原理

一、axios简介

  • axios是什么?
  • axios有什么特性?(不得不说面试被问到几次)

二、基本使用方式

三、实现axios和axios.method

四、请求和响应拦截器

  • 首先
  • 接下来,再执行
  • 接下来,再执行

 

一、axios简介
axios是什么?
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

axios有什么特性?(不得不说面试被问到几次)

  • 从浏览器中创建 XMLHttpRequests从 node.js
  • 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应转换请求数据和响应数据
  • 取消请求
  • 自动转换JSON 数据
  • 客户端支持防御 XSRF

实际上,axios可以用在浏览器和 node.js 中是因为,它会自动判断当前环境是什么,如果是浏览器,就会基于XMLHttpRequests实现axios。如果是node.js环境,就会基于node内置核心模块http实现axios简单来说,axios的基本原理就是

  1. axios还是属于 XMLHttpRequest, 因此需要实现一个ajax。或者基于http 。
  2. 还需要一个promise对象来对结果进行处理。

有什么不理解的或者是建议欢迎评论提出.项目已经放到github.可以的话给个star吧!谢谢 github:https://github.com/Sunny-lucking/howToBuildMyAxios

二、基本使用方式
axios基本使用方式主要有

  1. axios(config)
  2. axios.method(url, data , config)
// index.html文件 
<html> 
<script type="text/javascript" src="./myaxios.js"></script> 
<body> 
<button class="btn">点我发送请求</button> 
<script> 
    document.querySelector('.btn').onclick = function() { 
        // 分别使用以下方法调用,查看myaxios的效果 
        axios.post('/postAxios', { 
          name'小美post' 
        }).then(res => { 
          console.log('postAxios 成功响应', res); 
        }) 
 
        axios({ 
          method: 'post'
          url: '/getAxios' 
        }).then(res => { 
          console.log('getAxios 成功响应', res); 
        }) 
    } 
</script> 
</body> 
</html> 
</html> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

三、实现axios和axios.method
从axios(config)的使用上可以看出导出的axios是一个方法。从axios.method(url, data , config)的使用可以看出导出的axios上或者原型上挂有get,post等方法。

实际上导出的axios就是一个Axios类中的一个方法。

如代码所以,核心代码是request。我们把request导出,就可以使用axios(config)这种形式来调用axios了。

class Axios { 
    constructor() { 
 
    } 
 
    request(config) { 
        return new Promise(resolve => { 
            const {url = '', method = 'get', data = {}} = config; 
            // 发送ajax请求 
            const xhr = new XMLHttpRequest(); 
            xhr.open(method, url, true); 
            xhr.onload = function() { 
                console.log(xhr.responseText) 
                resolve(xhr.responseText); 
            } 
            xhr.send(data); 
        }) 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

怎么导出呢?十分简单,new Axios,获得axios实例,再获得实例上的request方法就好了。

// 最终导出axios的方法,即实例的request方法 
function CreateAxiosFn() { 
    let axios = new Axios(); 
    let req = axios.request.bind(axios); 
    return req; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
// 得到最后的全局变量axios 
let axios = CreateAxiosFn(); 
  • 1.
  • 2.

点击查看此时的myAxios.js

现在axios实际上就是request方法。

你可能会很疑惑,因为我当初看源码的时候也很疑惑:干嘛不直接写个request方法,然后导出呢?非得这样绕这么大的弯子。别急。后面慢慢就会讲到。

现在一个简单的axios就完成了,我们来引入myAxios.js文件并测试一下可以使用不?

简单的搭建服务器:

//server.js 
var express = require('express'); 
var app = express(); 
 
//设置允许跨域访问该服务. 
app.all('*'function (req, res, next) { 
    res.header('Access-Control-Allow-Origin''*'); 
    res.header('Access-Control-Allow-Headers''Content-Type'); 
    res.header('Access-Control-Allow-Methods''*'); 
    res.header('Content-Type''application/json;charset=utf-8'); 
    next(); 
}); 
 
app.get('/getTest'function(request, response){ 
    data = { 
        'FrontEnd':'前端'
        'Sunny':'阳光' 
    }; 
    response.json(data); 
}); 
var server = app.listen(5000, function(){ 
    console.log("服务器启动"); 
}); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
//index.html 
<script type="text/javascript" src="./myAxios.js"></script> 
 
<body> 
<button class="btn">点我发送请求</button> 
<script> 
    document.querySelector('.btn').onclick = function() { 
        // 分别使用以下方法调用,查看myaxios的效果 
        axios({ 
          method: 'get'
          url: 'http://localhost:5000/getTest' 
        }).then(res => { 
          console.log('getAxios 成功响应', res); 
        }) 
    } 
</script> 
</body> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

点击按钮,看看是否能成功获得数据。

发现确实成功。

可喜可贺

现在我们来实现下axios.method()的形式。

思路:我们可以再Axios.prototype添加这些方法。而这些方法内部调用request方法即可,如代码所示:

// 定义get,post...方法,挂在到Axios原型上 
const methodsArr = ['get''delete''head''options''put''patch''post']; 
methodsArr.forEach(met => { 
    Axios.prototype[met] = function() { 
        console.log('执行'+met+'方法'); 
        // 处理单个方法 
        if (['get''delete''head''options'].includes(met)) { // 2个参数(url[, config]) 
            return this.request({ 
                method: met, 
                url: arguments[0], 
                ...arguments[1] || {} 
            }) 
        } else { // 3个参数(url[,data[,config]]) 
            return this.request({ 
                method: met, 
                url: arguments[0], 
                data: arguments[1] || {}, 
                ...arguments[2] || {} 
            }) 
        } 
 
    } 
}) 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

我们通过遍历methodsArr数组,依次在Axios.prototype添加对应的方法,注意的是'get', 'delete', 'head', 'options'这些方法只接受两个参数。而其他的可接受三个参数,想一下也知道,get不把参数放body的。

但是,你有没有发现,我们只是在Axios的prototype上添加对应的方法,我们导出去的可是request方法啊,那怎么办?简单,把Axios.prototype上的方法搬运到request上即可。

我们先来实现一个工具方法,实现将b的方法混入a;

const utils = { 
  extend(a,b, context) { 
    for(let key in b) { 
      if (b.hasOwnProperty(key)) { 
        if (typeof b[key] === 'function') { 
          a[key] = b[key].bind(context); 
        } else { 
          a[key] = b[key
        } 
      } 
       
    } 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

然后我们就可以利用这个方法将Axios.prototype上的方法搬运到request上啦。

我们修改一下之前的CreateAxiosFn方法即可

function CreateAxiosFn() { 
  let axios = new Axios(); 
   
  let req = axios.request.bind(axios); 
  增加代码 
  utils.extend(req, Axios.prototype, axios) 
   
  return req; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

点击查看此时的myAxios.js

现在来测试一下能不能使用axios.get()这种形式调用axios。

<body> 
<button class="btn">点我发送请求</button> 
<script> 
    document.querySelector('.btn').onclick = function() { 
 
        axios.get('http://localhost:5000/getTest'
            .then(res => { 
                 console.log('getAxios 成功响应', res); 
            }) 
 
    } 
</script> 
</body> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

又是意料之中成功。再完成下一个功能之前,先给上目前myAxios.js的完整代码

class Axios { 
    constructor() { 
 
    } 
 
    request(config) { 
        return new Promise(resolve => { 
            const {url = '', method = 'get', data = {}} = config; 
            // 发送ajax请求 
            console.log(config); 
            const xhr = new XMLHttpRequest(); 
            xhr.open(method, url, true); 
            xhr.onload = function() { 
                console.log(xhr.responseText) 
                resolve(xhr.responseText); 
            } 
            xhr.send(data); 
        }) 
    } 

 
// 定义get,post...方法,挂在到Axios原型上 
const methodsArr = ['get''delete''head''options''put''patch''post']; 
methodsArr.forEach(met => { 
    Axios.prototype[met] = function() { 
        console.log('执行'+met+'方法'); 
        // 处理单个方法 
        if (['get''delete''head''options'].includes(met)) { // 2个参数(url[, config]) 
            return this.request({ 
                method: met, 
                url: arguments[0], 
                ...arguments[1] || {} 
            }) 
        } else { // 3个参数(url[,data[,config]]) 
            return this.request({ 
                method: met, 
                url: arguments[0], 
                data: arguments[1] || {}, 
                ...arguments[2] || {} 
            }) 
        } 
 
    } 
}) 
 
 
// 工具方法,实现b的方法或属性混入a; 
// 方法也要混入进去 
const utils = { 
  extend(a,b, context) { 
    for(let key in b) { 
      if (b.hasOwnProperty(key)) { 
        if (typeof b[key] === 'function') { 
          a[key] = b[key].bind(context); 
        } else { 
          a[key] = b[key
        } 
      } 
       
    } 
  } 

 
 
// 最终导出axios的方法-》即实例的request方法 
function CreateAxiosFn() { 
    let axios = new Axios(); 
 
    let req = axios.request.bind(axios); 
    // 混入方法, 处理axios的request方法,使之拥有get,post...方法 
    utils.extend(req, Axios.prototype, axios) 
    return req; 

 
// 得到最后的全局变量axios 
let axios = CreateAxiosFn(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.

四、请求和响应拦截器
我们先看下拦截器的使用

// 添加请求拦截器 
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); 
  }); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

拦截器是什么意思呢?其实就是在我们发送一个请求的时候会先执行请求拦截器的代码,然后再真正地执行我们发送的请求,这个过程会对config,也就是我们发送请求时传送的参数进行一些操作。

而当接收响应的时候,会先执行响应拦截器的代码,然后再把响应的数据返回来,这个过程会对response,也就是响应的数据进行一系列操作。

怎么实现呢?需要明确的是拦截器也是一个类,管理响应和请求。因此我们先实现拦截器

class InterceptorsManage { 
  constructor() { 
    this.handlers = []; 
  } 
 
  use(fullfield, rejected) { 
    this.handlers.push({ 
      fullfield, 
      rejected 
    }) 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

我们是用这个语句axios.interceptors.response.use和axios.interceptors.request.use,来触发拦截器执行use方法的。

说明axios上有一个响应拦截器和一个请求拦截器。那怎么实现Axios呢?看代码

class Axios { 
    constructor() { 
        新增代码 
        this.interceptors = { 
            request: new InterceptorsManage, 
            response: new InterceptorsManage 
        } 
    } 
 
    request(config) { 
        return new Promise(resolve => { 
            const {url = '', method = 'get', data = {}} = config; 
            // 发送ajax请求 
            console.log(config); 
            const xhr = new XMLHttpRequest(); 
            xhr.open(method, url, true); 
            xhr.onload = function() { 
                console.log(xhr.responseText) 
                resolve(xhr.responseText); 
            }; 
            xhr.send(data); 
        }) 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

可见,axios实例上有一个对象interceptors。这个对象有两个拦截器,一个用来处理请求,一个用来处理响应。

所以,我们执行语句axios.interceptors.response.use和axios.interceptors.request.use的时候,实现获取axios实例上的interceptors对象,然后再获取response或request拦截器,再执行对应的拦截器的use方法。

而执行use方法,会把我们传入的回调函数push到拦截器的handlers数组里。

到这里你有没有发现一个问题。这个interceptors对象是Axios上的啊,我们导出的是request方法啊(欸?好熟悉的问题,上面提到过哈哈哈~~~额)。处理方法跟上面处理的方式一样,都是把Axios上的方法和属性搬到request过去,也就是遍历Axios实例上的方法,得以将interceptors对象挂载到request上。

所以只要更改下CreateAxiosFn方法即可。

function CreateAxiosFn() { 
  let axios = new Axios(); 
   
  let req = axios.request.bind(axios); 
  // 混入方法, 处理axios的request方法,使之拥有get,post...方法 
  utils.extend(req, Axios.prototype, axios) 
  新增代码 
  utils.extend(req, axios) 
  return req; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

好了,现在request也有了interceptors对象,那么什么时候拿interceptors对象中的handler之前保存的回调函数出来执行。

没错,就是我们发送请求的时候,会先获取request拦截器的handlers的方法来执行。再执行我们发送的请求,然后获取response拦截器的handlers的方法来执行。

因此,我们要修改之前所写的request方法 之前是这样的。

request(config) { 
    return new Promise(resolve => { 
        const {url = '', method = 'get', data = {}} = config; 
        // 发送ajax请求 
        console.log(config); 
        const xhr = new XMLHttpRequest(); 
        xhr.open(method, url, true); 
        xhr.onload = function() { 
            console.log(xhr.responseText) 
            resolve(xhr.responseText); 
        }; 
        xhr.send(data); 
    }) 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

但是现在request里不仅要执行发送ajax请求,还要执行拦截器handlers中的回调函数。所以,最好下就是将执行ajax的请求封装成一个方法

request(config) { 
    this.sendAjax(config) 

sendAjax(config){ 
    return new Promise(resolve => { 
        const {url = '', method = 'get', data = {}} = config; 
        // 发送ajax请求 
        console.log(config); 
        const xhr = new XMLHttpRequest(); 
        xhr.open(method, url, true); 
        xhr.onload = function() { 
            console.log(xhr.responseText) 
            resolve(xhr.responseText); 
        }; 
        xhr.send(data); 
    }) 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

好了,现在我们要获得handlers中的回调

request(config) { 
    // 拦截器和请求组装队列 
    let chain = [this.sendAjax.bind(this), undefined] // 成对出现的,失败回调暂时不处理 
 
    // 请求拦截 
    this.interceptors.request.handlers.forEach(interceptor => { 
        chain.unshift(interceptor.fullfield, interceptor.rejected) 
    }) 
 
    // 响应拦截 
    this.interceptors.response.handlers.forEach(interceptor => { 
        chain.push(interceptor.fullfield, interceptor.rejected) 
    }) 
 
    // 执行队列,每次执行一对,并给promise赋最新的值 
    let promise = Promise.resolve(config); 
    while(chain.length > 0) { 
        promise = promise.then(chain.shift(), chain.shift()) 
    } 
    return promise; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

我们先把sendAjax请求和undefined放进了chain数组里,再把请求拦截器的handlers的成对回调放到chain数组头部。再把响应拦截器的handlers的承兑回调反倒chain数组的尾部。

然后再 逐渐取数 chain数组的成对回调执行。

promise = promise.then(chain.shift(), chain.shift()) 
  • 1.

这一句,实际上就是不断将config从上一个promise传递到下一个promise,期间可能回调config做出一些修改。什么意思?我们结合一个例子来讲解一下

首先拦截器是这样使用的

// 添加请求拦截器 
 
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); 
  }); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

然后执行request的时候。chain数组的数据是这样的

chain = [ 
  function (config) { 
    // 在发送请求之前做些什么 
    return config; 
  },  
   
  function (error) { 
    // 对请求错误做些什么 
    return Promise.reject(error); 
  } 
  this.sendAjax.bind(this),  
   
  undefined, 
   
  function (response) { 
    // 对响应数据做点什么 
    return response; 
  },  
  function (error) { 
    // 对响应错误做点什么 
    return Promise.reject(error); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

首先
执行第一次promise.then(chain.shift(), chain.shift()),即

promise.then
  function (config) { 
    // 在发送请求之前做些什么 
    return config; 
  },  
   
  function (error) { 
    // 对请求错误做些什么 
    return Promise.reject(error); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

一般情况,promise是resolved状态,是执行成功回调的,也就是执行

function (config) { 
    // 在发送请求之前做些什么 
    return config; 
  },  
  • 1.
  • 2.
  • 3.
  • 4.

promise.then是要返回一个新的promise对象的。为了区分,在这里,我会把这个新的promise对象叫做第一个新的promise对象 这个第一个新的promise对象会把

function (config) { 
    // 在发送请求之前做些什么 
    return config; 
  },  
  • 1.
  • 2.
  • 3.
  • 4.

的执行结果传入resolve函数中

resolve(config) 
  • 1.

使得这个返回的第一个新的promise对象的状态为resovled,而且第一个新的promise对象的data为config。

这里需要对Promise的原理足够理解。所以我前一篇文章写的是手写Promise核心原理,再也不怕面试官问我Promise原理,你可以去看看

接下来,再执行

promise.then
  sendAjax(config) 
  , 
  undefined 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

注意:这里的promise是 上面提到的第一个新的promise对象。

而promise.then这个的执行又会返回第二个新的promise对象。

因为这里promise.then中的promise也就是第一个新的promise对象的状态是resolved的,所以会执行sendAjax()。而且会取出第一个新的promise对象的data 作为config转入sendAjax()。

当sendAjax执行完,就会返回一个response。这个response就会保存在第二个新的promise对象的data中。

接下来,再执行

promise.then
  function (response) { 
    // 对响应数据做点什么 
    return response; 
  },  
  function (error) { 
    // 对响应错误做点什么 
    return Promise.reject(error); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

同样,会把第二个新的promise对象的data取出来作为response参数传入

function (response) { 
    // 对响应数据做点什么 
    return response; 
  },  
  • 1.
  • 2.
  • 3.
  • 4.

饭后返回一个promise对象,这个promise对象的data保存了这个函数的执行结果,也就是返回值response。

然后通过return promise;

把这个promise返回了。咦?是怎么取出promise的data的。我们看看我们平常事怎么获得响应数据的

axios.get('http://localhost:5000/getTest'
    .then(res => { 
         console.log('getAxios 成功响应', res); 
    }) 
  • 1.
  • 2.
  • 3.
  • 4.

在then里接收响应数据。所以原理跟上面一样,将返回的promise的data作为res参数了。

现在看看我们的myAxios完整代码吧,好有个全面的了解

class InterceptorsManage { 
    constructor() { 
        this.handlers = []; 
    } 
 
    use(fullfield, rejected) { 
        this.handlers.push({ 
            fullfield, 
            rejected 
        }) 
    } 

 
class Axios { 
    constructor() { 
        this.interceptors = { 
            request: new InterceptorsManage, 
            response: new InterceptorsManage 
        } 
    } 
 
    request(config) { 
        // 拦截器和请求组装队列 
        let chain = [this.sendAjax.bind(this), undefined] // 成对出现的,失败回调暂时不处理 
 
        // 请求拦截 
        this.interceptors.request.handlers.forEach(interceptor => { 
            chain.unshift(interceptor.fullfield, interceptor.rejected) 
        }) 
 
        // 响应拦截 
        this.interceptors.response.handlers.forEach(interceptor => { 
            chain.push(interceptor.fullfield, interceptor.rejected) 
        }) 
 
        // 执行队列,每次执行一对,并给promise赋最新的值 
        let promise = Promise.resolve(config); 
        while(chain.length > 0) { 
            promise = promise.then(chain.shift(), chain.shift()) 
        } 
        return promise; 
    } 
    sendAjax(){ 
        return new Promise(resolve => { 
            const {url = '', method = 'get', data = {}} = config; 
            // 发送ajax请求 
            console.log(config); 
            const xhr = new XMLHttpRequest(); 
            xhr.open(method, url, true); 
            xhr.onload = function() { 
                console.log(xhr.responseText) 
                resolve(xhr.responseText); 
            }; 
            xhr.send(data); 
        }) 
    } 

 
// 定义get,post...方法,挂在到Axios原型上 
const methodsArr = ['get''delete''head''options''put''patch''post']; 
methodsArr.forEach(met => { 
    Axios.prototype[met] = function() { 
        console.log('执行'+met+'方法'); 
        // 处理单个方法 
        if (['get''delete''head''options'].includes(met)) { // 2个参数(url[, config]) 
            return this.request({ 
                method: met, 
                url: arguments[0], 
                ...arguments[1] || {} 
            }) 
        } else { // 3个参数(url[,data[,config]]) 
            return this.request({ 
                method: met, 
                url: arguments[0], 
                data: arguments[1] || {}, 
                ...arguments[2] || {} 
            }) 
        } 
 
    } 
}) 
 
 
// 工具方法,实现b的方法混入a; 
// 方法也要混入进去 
const utils = { 
    extend(a,b, context) { 
        for(let key in b) { 
            if (b.hasOwnProperty(key)) { 
                if (typeof b[key] === 'function') { 
                    a[key] = b[key].bind(context); 
                } else { 
                    a[key] = b[key
                } 
            } 
 
        } 
    } 

 
 
// 最终导出axios的方法-》即实例的request方法 
function CreateAxiosFn() { 
    let axios = new Axios(); 
 
    let req = axios.request.bind(axios); 
    // 混入方法, 处理axios的request方法,使之拥有get,post...方法 
    utils.extend(req, Axios.prototype, axios) 
    return req; 

 
// 得到最后的全局变量axios 
let axios = CreateAxiosFn(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.

来测试下拦截器功能是否正常

<script type="text/javascript" src="./myAxios.js"></script> 
 
<body> 
<button class="btn">点我发送请求</button> 
<script> 
    // 添加请求拦截器 
    axios.interceptors.request.use(function (config) { 
        // 在发送请求之前做些什么 
        config.method = "get"
        console.log("被我请求拦截器拦截了,哈哈:",config); 
        return config; 
    }, function (error) { 
        // 对请求错误做些什么 
        return Promise.reject(error); 
    }); 
 
    // 添加响应拦截器 
    axios.interceptors.response.use(function (response) { 
        // 对响应数据做点什么 
        console.log("被我响应拦截拦截了,哈哈 "); 
        response = {message:"响应数据被我替换了,啊哈哈哈"
        return response; 
    }, function (error) { 
        // 对响应错误做点什么 
        console.log("错了吗"); 
        return Promise.reject(error); 
    }); 
    document.querySelector('.btn').onclick = function() { 
        // 分别使用以下方法调用,查看myaxios的效果 
        axios({ 
          url: 'http://localhost:5000/getTest' 
        }).then(res => { 
          console.log('response', res); 
        }) 
    } 
</script> 
</body> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

拦截成功!!!!!

 

责任编辑:姜华 来源: 前端阳光
相关推荐

2020-11-24 07:48:32

React

2021-08-10 18:36:02

Express原理面试

2022-08-27 13:49:36

ES7promiseresolve

2020-10-23 09:26:57

React-Redux

2021-05-08 07:53:33

面试线程池系统

2022-04-01 07:52:42

JavaScript防抖节流

2020-12-03 08:14:45

Axios核心Promise

2022-10-31 11:10:49

Javavolatile变量

2021-11-24 10:10:32

axios前端拦截器

2023-11-28 17:49:51

watch​computed​性能

2020-10-15 12:52:46

SpringbootJava编程语言

2021-04-22 07:49:51

Vue3Vue2.xVue3.x

2024-03-05 10:33:39

AOPSpring编程

2024-08-22 10:39:50

@Async注解代理

2020-12-09 10:29:53

SSH加密数据安全

2020-11-02 09:35:04

ReactHook

2021-12-02 08:19:06

MVCC面试数据库

2024-02-29 16:49:20

volatileJava并发编程

2024-08-12 17:36:54

2024-08-29 16:30:27

点赞
收藏

51CTO技术栈公众号