Axios

应用

axios能够用于浏览器端和node端吗?这是什么设计模式?

可以的,是基于适配器模式,能使接口不兼容的对象能够相互合作。参考design - 设计模式(以Typescript描述)

其实现:

在浏览器端如何实现的

在nodejs端如何实现的

axios怎么取消请求

axios 如何取消一个请求提供了两种使用模式:

第一种 调用CancelToken的静态方法source

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})
source.cancel('Operation canceled by the user.');

第二种 自己实例化:

let cancel;
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});
cancel();

OK,可以看到使用非常简单,两种使用方式道理是一样的,分两步:

获取cancelToken实例,注入请求的配置参数

需要取消的时候,调用 提供的cancel方法.

参考:

axios的cancelToken取消机制原理 - SegmentFault 思否

页面切换后,需要将当前页面中的接口都取消,应该怎么做?

在vuex中维护一个请求队列

export default {
    state: {
        cancelTokenArr:[] // 存储cancel token
    },
    mutations: {
        addCancelToken({cancelTokenArr},data){
            cancelTokenArr.push(data)
        },
        clearCancelToken(state){
            state.cancelTokenArr.map(item => {
                item.cancel(`${item.url}---路由切换取消请求`)
            })
            state.cancelTokenArr = []
        }
    }
}

请求发出前,利用拦截器将取消的cancel函数与当次url利用addCancelToken存储到内存(vuex)中

import axios from 'axios'
import store from '../store'

// 请求拦截器
axios.interceptors.request.use(config => {
   // 请求发出时,添加到cancelTokenArr中
   config.cancelToken = new axios.CancelToken(e => {
       store.commit('addCancelToken', {
           cancel: e,
           url: location.host + config.url
       })
    })
  })
  return config
}, error => {
  Message.error('未知错误')
  return Promise.reject(error)
}

请求发出后,利用响应拦截器处理取消请求

axios.interceptors.response.use(response => {...},error => {
  // 这里判断异常情况,如果axios.isCancel 为 true时,说明请求被取消
  if (axios.isCancel(error)) {
    // 请求取消
    console.warn(error)
    console.table([error.message.split('---')[0]], 'cancel')
  } else {...}
}

利用router.beforeEach切换路由时取消当前pending中的请求

// 切换路由时取消正在pending的请求
router.beforeEach((to, from, next) => {
   store.commit('clearCancelToken')
   next()   
}

参考:

Axios利用拦截器取消页面切换pending中的请求_一只路过的小码农-CSDN博客

原理

拦截器执行顺序是怎么样的?原理是什么?

拦截器执行顺序如图:

首先axios会通过InterceptorManager类来管理拦截器:

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  // 拦截器
  // 为request和response分别定义一个InterceptorManager
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

我们使用use时,会将回调传入handler数组

InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  // 注册拦截器到handlers数组中,将正常和异常回调函数传入
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

最后执行时通过职责链模式,以拦截器组装职责链:

// 职责链模式
// 执行链的第一条就是dispatchRequest,通过适配器取到当前环境的请求方法
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
// 将请求拦截器一个个的加到执行链的前面,所以请求拦截器2在请求拦截器1前面执行
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
  chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 将响应拦截器一个个的加到执行链的后面,所以响应拦截器1在响应拦截器2前面执行
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
  chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 前置执行promise
while (chain.length) {
  promise = promise.then(chain.shift(), chain.shift());
}

参考:

FunnyLiu/axios at readsource

在浏览器端如何实现的

主要是使用XMLHttpRequest对象

  1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象.
  2. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
  3. 设置响应HTTP请求状态变化的函数.
  4. 发送HTTP请求.
  5. 获取异步调用返回的数据.
  6. 使用JavaScript和DOM实现局部刷新.

在nodejs端如何实现的

主要是通过http模板实现的

cancelToken的原理是什么?

最底层还是基于XmlHttpRequest实例的abort方法,如果是fetch的话则是AboutController。

参考:

axios的cancelToken取消机制原理 - SegmentFault 思否