浅析Axios拦截器原理🎁

597 阅读3分钟

别学拉,别学拉,学不动拉~

前言

Ajax的横空出世使静态面过渡到了动态页面,让页面姿态百放。大家最初停留在jquery时代封装的框架里,随着时间的推移,市场涌入多种具有不同色彩的框架,如今的React、Vue都占据着前端库架构的一席之地,今天就来讲讲做出卓越贡献的Ajax库函数 --- Axios

正文

Axios

Axios是一个基于Promise封装HTTP请求工具,适配于Web端与Node端,它拥有三大功能:

1. 适配器
   web端使用XMLHttpRequest
   Node端根据当前协议会使用http模块与https模块

2. 拦截器
   通过Promise链式调用,在真正请求之前与之后添加钩子函数

3. 抵御CSRF安全问题
   内部设置xsrfName字段可以提供给开发者传递token

这节我们模拟下Axios的拦截器是怎么实现的

Axios拦截器使用

我们先来回忆下Axios的拦截器是怎么使用的 执行以上代码,可以得出以下打印顺序:

'request-resolved'
'result', result
'response-resolved'

以上是一切正常的情况下会按照这个顺序链式执行

但是一旦发生错误执行到reject函数的情况下,后续的链式回调函数就会被劫持掉,不再往下执行

原理分析

拦截器具有以下几个特点:

1. 第一部分介绍了Axios是基于Promise的HTTP请求工具,那么自然interceptors.request注入的fulfilled函数与rejected函数会在真正请求的函数之前,同理intercptors.response会被放置在后面
2. 支持拦截器添加多个
3. 一旦reject后就不会继续往下执行

开始模拟

  • 首先定义拦截器构造函数,它应该具备一个存储拦截函数的无序集合和一个添加拦截函数的方法

  • 其次再定义Axios构造函数去承载拦截器的实例,方便axios实例可以去调用,在Axios原型下定义Axios合并拦截器与请求实体函数的方法, 然后我们可以看到在request方法里,defaultRequest是真正的请求实体,它被定义在chain里,是以数组形式并且后面跟了void 0,是为了匹配拦截器的存储拦截函数的形式

  • 随后会去获取最新的拦截器的拦截函数,并将request添加到chain之前response添加到chain之后

  • 最后循环chain数组,将其从头到尾取出,并且一一执行,然后我们可以发现这里请求实体是被放置在fulfilled对应的位置,所以一旦request拦截器最后一个收集到的fulfullied函数发生错误就会跳过defaultRequest函数而去执行then(void 0)

模拟源码

function InterceptorManage() {
   this.handlers = []
}

InterceptorManage.prototype.use = function(fulfilled, rejected) {
   this.handlers.push(fulfilled, rejected)
}
    
function Axios() {
  this.interceptors = {
    request: new InterceptorManage(),
    response: new InterceptorManage()
  }
}

Axios.prototype.request = function() {
  const chain = [this.defaultRequest.bind(this, ...arguments), void 0]
  const { request, response } = this.interceptors
  chain.unshift(...request.handlers)
  chain.push(...response.handlers)

  let promise = Promise.resolve()
  while (chain.length) {
    promise.then(chain.shift(), chain.shift())
  }
  return promise
}

Axios.prototype.defaultRequest = function(method, url, data, config) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open(method.toUpperCase(), url)
       
    xhr.onreadystatechange = function(response) {
      const target = response.target
      if (target.status === 200 && target.readyState === 4) {
        resolve(target.responseText)
      } else {
        reject(target.responseText)
      }
    }
    xhr.send(data || null)
  })
}
    
Axios.prototype.get = function() {
  this.request.call(this, 'get', ...arguments)
}

const axios = new Axios()

axios.interceptors.request.use(function() {
  console.log('requestM1')/* 2021年03月12日 17时37分49秒 */
}, function() {
  console.log('requestr')/* 2021年03月12日 17时37分54秒 */
})

axios.interceptors.request.use(function() {
  console.log('requestM')/* 2021年03月12日 17时37分49秒 */
}, function() {
  console.log('requestr')/* 2021年03月12日 17时37分54秒 */
})

axios.interceptors.response.use(function() {
  console.log('responseM')/* 2021年03月12日 17时37分49秒 */
}, function() {
  console.log('responseR')/* 2021年03月12日 17时37分54秒 */
})
   
axios.get('www.google.com')

往期地址

🎁javascript面试指南(一)

🎁javascript面试指南(二)

🎁javascript技巧(一)

🎁javascript技巧(二)

🎁html凡尔赛技巧

🎁移动端常见问题

🎁FLIP思想解决Vue官网动画

结束语

看的开心的可以给个关注哦,嘻嘻

欢迎添加我的微信进群,群里会有一些福利的

微信号: IAmFineThanksMartin