如何实现跳转页面之后,取消当前页面还未完成的请求?

在前端开发中,当页面跳转时取消当前页面未完成的请求是一个常见的需求。以下是几种主流框架的解决方案,结合通用的 AbortController API 实现这一功能。

1. 使用 AbortController(通用方案)

AbortController 是浏览器原生提供的 API,可以取消未完成的异步请求(如 fetch 或 XMLHttpRequest)。以下是核心思路:

步骤:
  1. 创建 AbortController 实例,并将其与页面或组件关联。
  2. 在请求时传递 signal,确保请求可以被取消。
  3. 在页面跳转或卸载时调用 abort(),取消所有未完成的请求。

示例代码(通用 JavaScript): 

// 创建全局或页面级的 AbortController
const controller = new AbortController();
const signal = controller.signal;

// 发送请求时传入 signal
fetch('/api/data', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('请求已取消');
    } else {
      console.error('请求失败:', error);
    }
  });

// 页面跳转时调用 abort()
// 例如在组件卸载时(React/Vue)或路由守卫中:
controller.abort();

2. React 中的实现

在 React 中,可以通过 useEffect 的清理函数或路由守卫(如 react-router)来实现。

方案 1:通过 useEffect 清理
import { useEffect, useRef } from 'react';

function MyComponent() {
  const controller = useRef(new AbortController());

  useEffect(() => {
    const signal = controller.current.signal;

    // 发送请求
    fetch('/api/data', { signal })
      .then(response => response.json())
      .then(data => console.log(data))
      .catch(error => {
        if (error.name === 'AbortError') return;
        console.error(error);
      });

    // 组件卸载时取消请求
    return () => {
      controller.current.abort();
    };
  }, []);

  return <div>Content...</div>;
}

 方案 2:通过 react-router 导航守卫

// 在路由配置中使用导航守卫
import { createBrowserRouter, Navigate } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/page',
    element: <MyComponent />,
    loader: async ({ signal }) => {
      const response = await fetch('/api/data', { signal });
      return response.json();
    },
    // 导航时自动取消未完成的请求
  },
]);

3. Vue 中的实现

在 Vue 中,可以使用 onUnmounted 钩子或 Vue Router 的导航守卫。

方案 1:通过 onUnmounted 钩子
<template>
  <div>Content...</div>
</template>

<script setup>
import { onUnmounted, ref } from 'vue';

const controller = ref(new AbortController());
const signal = controller.value.signal;

// 发送请求
fetch('/api/data', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') return;
    console.error(error);
  });

// 组件卸载时取消请求
onUnmounted(() => {
  controller.value.abort();
});
</script>
方案 2:通过 Vue Router 导航守卫
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/page',
      component: MyComponent,
      beforeEnter: (to, from, next) => {
        // 在进入新路由前,取消当前页面的请求
        // 需要将 controller 存储在全局或组件中
        if (currentController) {
          currentController.abort();
        }
        next();
      },
    },
  ],
});

4. 使用 Axios 的 CancelToken(替代方案)

如果使用 Axios,可以通过 CancelToken 实现类似功能:

示例代码:
// 创建 CancelToken 源
const CancelToken = axios.CancelToken;
let cancel;

// 发送请求
axios.get('/api/data', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
})
.catch(thrown => {
  if (axios.isCancel(thrown)) {
    console.log('请求已取消:', thrown.message);
  } else {
    // 处理错误
  }
});

// 页面跳转时取消请求
cancel('页面跳转,取消请求');

5. 全局管理请求(推荐复杂场景)

对于复杂应用,可以维护一个全局的请求管理器:

 ‌请求标识收集
在请求拦截器中为每个请求创建唯一标识(如 cancelToken 或 AbortController),并将取消函数存储到全局容器(如 Vuex、Redux 或独立事件总线)中‌。

// Axios 拦截器示例(Vue/React 通用)
service.interceptors.request.use(config => {
  const source = axios.CancelToken.source();
  config.cancelToken = source.token;
  store.commit('addRequest', source.cancel);  // 存储取消函数到全局状态‌
  return config;
});

路由跳转触发清理
在路由守卫(Vue Router)或组件卸载钩子(React)中遍历全局容器,执行所有未完成请求的取消操作‌。

// Vue Router 守卫示例
router.beforeEach((to, from, next) => {
  store.state.requestList.forEach(cancel => cancel('Route changed'));  // 取消所有请求‌
  store.commit('clearRequests');  // 清空容器
  next();
});

6.技术栈差异化实现

框架实现方案代码示例
Vue通过 Vuex 存储请求取消函数,结合 router.beforeEach 触发清理store.commit('pushPmsStack', cancel) + 路由守卫遍历清理‌
React使用全局事件总线(如 eventBus)存储请求标识,在组件卸载时执行清理eventBus.cancelTokenList.push(cancel) + useEffect 清理函数‌

7.通用代码实现(Axios 适配)

 ‌1.创建可取消请求实例

// 创建 Axios 实例并配置拦截器
const service = axios.create();
let pendingRequests = new Map();  // 使用 Map 存储请求标识‌

service.interceptors.request.use(config => {
  const requestKey = `${config.method}_${config.url}`;
  const controller = new AbortController();
  config.signal = controller.signal;
  pendingRequests.set(requestKey, controller.abort);  // 存储取消函数‌
  return config;
});

 ‌2.路由跳转时批量取消

// 路由跳转逻辑(通用)
const handleRouteChange = () => {
  pendingRequests.forEach(abort => abort('Navigation occurred'));  // 执行所有取消操作‌
  pendingRequests.clear();  // 清空存储
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值