问题

在项目中,登录后用户所生成的token会有失效的时候(目前pc端和小程序共用一个token,双端登录会重新生成token)
进入页面时,axios会同时发起多个异步请求
Snipaste_2022-11-19_21-07-29.png
当用户进入用户面板时,才会校验用户的token是否有效,所以就会出现下面这幕。。。
Snipaste_2022-11-18_21-42-04.png

构思

该如何才能解决这么多奇怪的error呢?而且error都是一致的。

有没有办法可以在已知一个请求后error为token失效,其余请求就中断?

解决办法

我们使用axios的拦截器和AbortController搭配来取消请求。

取消请求  Axios 中文文档 Axios 中文网
AbortController - Web API 接口参考 _ MDN

中文网只给了一个非常简单的例子,但是看不懂
Snipaste_2022-11-19_21-14-24.png
下面分别讲一下我对拦截器和AbortController的理解。

拦截器

拦截器分为请求拦截器相应拦截器

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

固定格式就是这样,具体要做什么往里面加就可以了。

已封装的axios对象添加拦截器
//将封装好的名字替换到上面例子的axios即可
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
AboutController

mdn的说法:AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。
(控制器对象,什么意思?????)

个人理解:AbortController 是一个工厂类,new出来的对象包含了一个令牌和众多方法,对象内包含一个abort方法,使用后会销毁令牌,也会停止所有绑定了该令牌的请求。

具体使用

上代码

//引入axios
import axios from "axios";
import router from "../router/index.js"
import {ElMessage} from "element-plus";

//新创建一个controller对象
var controller = new AbortController();

const request = axios.create({
    baseURL: '/back',
    timeout: 5000
})

//相应拦截器
request.interceptors.response.use(function (res){
    //此为token失效的错误代码,后端自定义
    if(res.data.errno===20006){
        ElMessage.error("登录态失效,请重新登录!")        //弹窗一次
        controller.abort()        //中断所有绑定了这个controller的请求
        controller = new AbortController(); //重新创建controller对象,看下面解释
        router.push("/login")
    }else{
        return res //没报错,直接返回对应数据
    }
})

export {request,controller};//export出封装的对象和controller,controller用于后面绑定到请求中。

AbortController 是一次性的,也就是说,如果我们abort了一次,
他就永远都不会再次请求了,所有请求都会处于abort状态。
所以abort后我们需要将所有请求的状态重置(通过new AbortController)

request封装

仅供举例

//引入request和controller对象
import {request,controller} from "@/utils/request";

export function loadHealthSuggest() {
    return request({
        url: '/api/healthsuggest',
        method: 'GET',
        headers: {
            'content-type': 'application/json',
            'User-tk': window.$cookies.get('userToken'),
            'User-id': window.$cookies.get('userId'),
        },
        signal: controller.signal //这里使用signal来绑定controller对象内的令牌
    })
}

所有request请求在封装时都添加这个signal属性,就可以实现同时中断多个请求了。

Snipaste_2022-11-19_21-43-02.png
呃。。好像还是弹窗了两次