vue路由跳转取消请求

741 阅读2分钟

如果服务器返回比较慢的时候,用户又频繁切换路由,就会导致很多请求在等待响应,就容易导致浏览器卡死。

如果我们在每次路由跳转时取消了这些请求,就能很好的规避这种情况。

我们请求使用axios来完成,axios为我们提供了取消请求的方法,具体如何获取请阅读官方文档或者下文,下文仅展示了一种获取方式。

第一步

首先思考我们要在路由跳转时去取消请求,那在路由跳转前我们是不是得先把这些请求存起来,所以我创建了一个pinia(vuex同理)

// /store/myRequest.js
import { defineStore } from 'pinia'

export default defineStore('myRequest', {
    state: () => ({
        // 取消请求token数组
        cancelTokens: []
    }),
    actions: {
        // 添加
        pushToken(token) {
            this.cancelTokens.push(token)
        },
        // 取消并清空请求
        cancelRequest() {
            if (this.cancelTokens.length === 0) return;
            this.cancelTokens.forEach(item => {
                item()
            })
            this.cancelTokens = []
        }
    }
})

第二步

然后我们在请求拦截器中去获取并存储请求的取消方法

// /request/http.js
import axios from 'axios'
import useMyRequest from '../store/myRequest'
const CancelToken = axios.CancelToken;
// 路由跳转不清除请求白名单,如果有那种全局定时刷新的请求,不需要清除的,可以其接口放在这里
const cancelWhite = ['/user/news/xxx']
const myRequestStore = useMyRequest()

// 创建axios实例
const service = axios.create({
    // ...省略
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 存请求-用于路由跳转时清空请求,可以配置白名单
    if (!cancelWhite.includes(config.url)) {
      config.cancelToken = new CancelToken((c) => {
        myRequestStore.pushToken(c)// 这里的c就是取消请求的方法
      })
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

最后一步

好,上面的新增和存储都写好了,我们要在哪里去调用取消请求呢?

我们上面说了要在路由跳转时清除请求,那当然是在路由拦截器中去调用了。

// /router/index.js
// 省略路由配置代码等...

import useMyRequest from '@/store/myRequest'

// 拦截器
router.beforeEach((to, from, next) => {
    // 取消请求
    const myRequestStore = useMyRequest()
    myRequestStore.cancelRequest()
})

其他

我们来测试一下,这里介绍一个浏览器的设置,可以把浏览器的网速调慢,网速慢了请求响应就慢了,方便我们测试。

image.png

这里有一些默认的配置,也可以自定义网速,这里就不多介绍了,我们把网速切换为Fast 3G来测试一下

image.png

当我快速切换两个路由时,上一个页面未完成的请求的状态为取消(canceled),证明我们成功啦。

补充

从 v0.22.0 开始,Axios 支持以 fetch API 方式—— AbortController 取消请求:

const controller = new AbortController(); 

axios.get('/foo/bar', { 
    signal: controller.signal 
}).then(function(response) { //... });

// 取消请求 
controller.abort()

所以我们上述的例子中,也可以改造为AbortController这种方式来实现。CancelToken这种方式会在Axiosv0.22.0版本后被弃用。