最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在Vue3項(xiàng)目中取消重復(fù)請(qǐng)求的兩種方案

 更新時(shí)間:2026年02月07日 09:58:42   作者:用戶3905133219288  
文章主要介紹了在Vue3項(xiàng)目中如何取消重復(fù)請(qǐng)求,以節(jié)省資源、保證數(shù)據(jù)正確性和提升用戶體驗(yàn),文章詳細(xì)講解了兩種實(shí)現(xiàn)方式:使用Axios的CancelToken和AbortController,需要的朋友可以參考下

今天我們來聊聊一個(gè)實(shí)際開發(fā)中常見的問題:在Vue3項(xiàng)目中,如何取消重復(fù)的請(qǐng)求。

為什么需要取消重復(fù)請(qǐng)求?

這樣做有幾個(gè)明顯的好處:

  1.  節(jié)省資源:釋放瀏覽器連接,減輕服務(wù)器壓力。
  2.  保證數(shù)據(jù)正確性:避免因請(qǐng)求響應(yīng)順序錯(cuò)亂導(dǎo)致頁面顯示舊數(shù)據(jù)。
  3.  提升用戶體驗(yàn):防止無效請(qǐng)求阻塞后續(xù)有效操作。

在Vue3的生態(tài)中,我們最常用的HTTP客戶端是 axios。因此,本文的核心將圍繞如何使用 axios 的取消令牌(CancelToken)及其更現(xiàn)代的替代品——AbortController——來實(shí)現(xiàn)請(qǐng)求取消。

方案一:使用 Axios 的 CancelToken

axios 從很早就支持通過 CancelToken 來取消請(qǐng)求。雖然它在 axios v0.22.0 之后被標(biāo)記為已棄用,轉(zhuǎn)而推薦使用標(biāo)準(zhǔn)的 AbortController,但很多現(xiàn)有項(xiàng)目仍在使用這個(gè)API,所以我們有必要了解一下。 它的工作原理是:創(chuàng)建一個(gè)“取消令牌”的源(source),將這個(gè)令牌配置到請(qǐng)求中。當(dāng)我們需要取消請(qǐng)求時(shí),調(diào)用這個(gè)源的 cancel 方法。

基本使用示例

import?axios?from?'axios';

// 1. 創(chuàng)建一個(gè) CancelToken Source
const?source = axios.CancelToken.source();

// 2. 發(fā)起請(qǐng)求,并配置 cancelToken
axios.get('/api/user/123', {
? cancelToken: source.token
}).then(response?=>?{
? console.log(response.data);
}).catch(function?(thrown) {
? // 判斷錯(cuò)誤是否是因?yàn)檎?qǐng)求被取消
? if?(axios.isCancel(thrown)) {
? ? console.log('請(qǐng)求被取消:', thrown.message);
? }?else?{
? ? // 處理其他錯(cuò)誤
? }
});

// 3. 在需要的時(shí)候取消請(qǐng)求
source.cancel('操作被用戶取消');

在Vue3組件中管理重復(fù)請(qǐng)求

在實(shí)際項(xiàng)目中,我們通常需要一種機(jī)制來管理“同一個(gè)”重復(fù)請(qǐng)求。一個(gè)常見的思路是:將每個(gè)請(qǐng)求的唯一標(biāo)識(shí)(例如:請(qǐng)求方法+URL)和一個(gè)取消函數(shù)關(guān)聯(lián)起來,在發(fā)起新請(qǐng)求前,檢查并取消舊的、未完成的相同請(qǐng)求。

我們可以利用Vue3的響應(yīng)式系統(tǒng)和組合式API,封裝一個(gè)可復(fù)用的邏輯。

首先,我們創(chuàng)建一個(gè)用于存儲(chǔ)所有進(jìn)行中請(qǐng)求的Map,以及相關(guān)的操作函數(shù)。

// utils/request.js
import?axios?from?'axios';

// 存儲(chǔ)所有進(jìn)行中請(qǐng)求的Map
const?pendingRequestMap =?new?Map();

/**
?* 生成請(qǐng)求的唯一標(biāo)識(shí)鍵
?*?@param?{*} config axios的請(qǐng)求配置對(duì)象
?*?@returns?{string} 唯一鍵
?*/
function?generateReqKey(config) {
? const?{ method, url, params, data } = config;
? // 簡單拼接,可根據(jù)業(yè)務(wù)復(fù)雜化(如對(duì)data排序)
? return?[method, url,?JSON.stringify(params),?JSON.stringify(data)].join('&');
}

/**
?* 添加請(qǐng)求到等待隊(duì)列
?*?@param?{*} config axios的請(qǐng)求配置對(duì)象
?*/
function?addPendingRequest(config) {
? const?requestKey =?generateReqKey(config);
? // 為這個(gè)請(qǐng)求創(chuàng)建一個(gè)取消令牌源
? config.cancelToken?= config.cancelToken?||?new?axios.CancelToken((cancel) =>?{
? ? // 如果Map中還沒有這個(gè)key,則添加進(jìn)去
? ? if?(!pendingRequestMap.has(requestKey)) {
? ? ? pendingRequestMap.set(requestKey, cancel);
? ? }
? });
}

/**
?* 移除等待隊(duì)列中的請(qǐng)求
?*?@param?{*} config axios的請(qǐng)求配置對(duì)象
?*/
function?removePendingRequest(config) {
? const?requestKey =?generateReqKey(config);
? if?(pendingRequestMap.has(requestKey)) {
? ? // 如果在Map中存在這個(gè)請(qǐng)求,說明它還未完成,將其取消并從Map中移除
? ? const?cancel = pendingRequestMap.get(requestKey);
? ? cancel(requestKey);?// 取消請(qǐng)求,可以傳遞消息
? ? pendingRequestMap.delete(requestKey);
? }
}

// 創(chuàng)建axios實(shí)例
const?service = axios.create({
? timeout:?10000,
});

// 請(qǐng)求攔截器:在請(qǐng)求發(fā)出前,檢查并取消重復(fù)請(qǐng)求
service.interceptors.request.use(
? (config) =>?{
? ? // 檢查并取消之前的相同請(qǐng)求
? ? removePendingRequest(config);
? ? // 將當(dāng)前請(qǐng)求添加到等待隊(duì)列
? ? addPendingRequest(config);
? ? return?config;
? },
? (error) =>?{
? ? return?Promise.reject(error);
? }
);

// 響應(yīng)攔截器:請(qǐng)求完成后(無論成功失?。瑢⑵鋸牡却?duì)列中移除
service.interceptors.response.use(
? (response) =>?{
? ? removePendingRequest(response.config);
? ? return?response;
? },
? (error) =>?{
? ? // 如果錯(cuò)誤是因?yàn)槿∠?qǐng)求造成的,我們選擇忽略這個(gè)錯(cuò)誤,不拋出到業(yè)務(wù)層
? ? if?(axios.isCancel(error)) {
? ? ? console.log('已取消的重復(fù)請(qǐng)求:', error.message);
? ? ? return?new?Promise(() =>?{});?// 返回一個(gè)“永遠(yuǎn)pending”的Promise,中斷Promise鏈
? ? }
? ? // 對(duì)于其他錯(cuò)誤,移除請(qǐng)求并正常拋出
? ? removePendingRequest(error.config?|| {});
? ? return?Promise.reject(error);
? }
);

export?default?service;

然后,在Vue組件中,你可以直接使用這個(gè)封裝好的 service 來發(fā)起請(qǐng)求。它會(huì)自動(dòng)處理重復(fù)請(qǐng)求的取消。

<script setup>
import?{ ref }?from?'vue';
import?request?from?'@/utils/request';

const?searchResults =?ref([]);
const?loading =?ref(false);

const?handleSearch?=?async?(keyword) => {
? loading.value?=?true;
? try?{
? ? const?response =?await?request.get('/api/search', {
? ? ? params: { keyword }
? ? });
? ? searchResults.value?= response.data;
? }?catch?(error) {
? ? // 這里不會(huì)捕獲到因重復(fù)請(qǐng)求被取消而拋出的錯(cuò)誤,因?yàn)槲覀冊(cè)跀r截器中已經(jīng)處理了
? ? console.error('搜索失敗:', error);
? }?finally?{
? ? loading.value?=?false;
? }
};
</script>

注意:CancelToken 方式在 axios 新版本中已被標(biāo)記為棄用。對(duì)于新項(xiàng)目,建議直接使用下面介紹的更現(xiàn)代的 AbortController 方案。

方案二: AbortController

AbortController 是一個(gè)現(xiàn)代的Web API,它提供了一種更通用、更標(biāo)準(zhǔn)的方式來中止一個(gè)或多個(gè)Web請(qǐng)求。fetch API 和新的 axios 版本都原生支持它。使用 AbortController 是當(dāng)前推薦的做法。

基本概念

  • AbortController:控制器對(duì)象,用于觸發(fā)中止信號(hào)。
  • AbortSignal:信號(hào)對(duì)象,關(guān)聯(lián)到具體的請(qǐng)求上??刂破骺梢酝ㄟ^它來通知請(qǐng)求“需要中止”。

基本使用示例

// 1. 創(chuàng)建一個(gè) AbortController 實(shí)例
const?controller =?new?AbortController();
const?signal = controller.signal;?// 獲取它的 signal

// 2. 發(fā)起 fetch 請(qǐng)求,并將 signal 關(guān)聯(lián)上去
fetch('/api/some-data', { signal })
? .then(response?=>?response.json())
? .then(data?=>?console.log(data))
? .catch(err?=>?{
? ? // 如果錯(cuò)誤是因?yàn)檎?qǐng)求被中止
? ? if?(err.name?===?'AbortError') {
? ? ? console.log('Fetch 請(qǐng)求被中止');
? ? }?else?{
? ? ? console.error('其他錯(cuò)誤:', err);
? ? }
? });

// 3. 在需要的時(shí)候中止請(qǐng)求
controller.abort();?// 這會(huì)觸發(fā) signal 的中止事件,從而取消請(qǐng)求

在 Axios 中使用 AbortController

從 axios v0.22.0 開始,你可以使用 signal 屬性來配置 AbortSignal。

import?axios?from?'axios';

const?controller =?new?AbortController();

axios.get('/api/user/123', {
? signal: controller.signal?// 將 signal 傳遞給請(qǐng)求配置
}).then(response?=>?{
? console.log(response.data);
}).catch(function?(error) {
? // 判斷錯(cuò)誤是否是因?yàn)檎?qǐng)求被取消
? if?(axios.isCancel(error)) {
? ? console.log('請(qǐng)求被取消:', error.message);
? }?else?{
? ? // 處理其他錯(cuò)誤
? }
});

// 取消請(qǐng)求
controller.abort();

封裝基于 AbortController 的重復(fù)請(qǐng)求取消

我們可以用類似的思路,用 AbortController 重構(gòu)之前的工具函數(shù)。主要變化是將存儲(chǔ)的 cancel 函數(shù)替換為 AbortController 實(shí)例。

// utils/request-abort.js
import?axios?from?'axios';

const?pendingRequestMap =?new?Map();

function?generateReqKey(config) {
? const?{ method, url, params, data } = config;
? return?[method, url,?JSON.stringify(params),?JSON.stringify(data)].join('&');
}

function?addPendingRequest(config) {
? const?requestKey =?generateReqKey(config);
? // 如果已有相同請(qǐng)求在進(jìn)行,則中止它
? if?(pendingRequestMap.has(requestKey)) {
? ? const?oldController = pendingRequestMap.get(requestKey);
? ? oldController.abort();?// 中止舊的請(qǐng)求
? ? pendingRequestMap.delete(requestKey);
? }
? // 為當(dāng)前請(qǐng)求創(chuàng)建新的控制器并存儲(chǔ)
? const?controller =?new?AbortController();
? config.signal?= controller.signal;?// 關(guān)鍵:將 signal 賦給請(qǐng)求配置
? pendingRequestMap.set(requestKey, controller);
}

function?removePendingRequest(config) {
? const?requestKey =?generateReqKey(config);
? if?(pendingRequestMap.has(requestKey)) {
? ? // 請(qǐng)求完成,直接從Map中移除即可,不需要手動(dòng)abort
? ? pendingRequestMap.delete(requestKey);
? }
}

const?service = axios.create({
? timeout:?10000,
});

service.interceptors.request.use(
? (config) =>?{
? ? removePendingRequest(config);?// 先移除可能存在的舊記錄(清理作用)
? ? addPendingRequest(config);?// 添加新請(qǐng)求
? ? return?config;
? },
? (error) =>?{
? ? return?Promise.reject(error);
? }
);

service.interceptors.response.use(
? (response) =>?{
? ? removePendingRequest(response.config);
? ? return?response;
? },
? (error) =>?{
? ? // 判斷錯(cuò)誤是否由取消請(qǐng)求導(dǎo)致
? ? if?(axios.isCancel(error)) {
? ? ? console.log('已取消的重復(fù)請(qǐng)求:', error.message);
? ? ? return?new?Promise(() =>?{});?// 中斷Promise鏈
? ? }
? ? removePendingRequest(error.config?|| {});
? ? return?Promise.reject(error);
? }
);

export?default?service;

這個(gè)版本的邏輯更清晰:在添加新請(qǐng)求時(shí),直接中止舊的相同請(qǐng)求。響應(yīng)攔截器里只需要做清理工作。

進(jìn)階與優(yōu)化

上面的方案解決了核心問題,但在實(shí)際項(xiàng)目中,我們可能還需要考慮更多細(xì)節(jié)。

1. 白名單控制

有些請(qǐng)求我們可能不希望被自動(dòng)取消。例如,一個(gè)輪詢定時(shí)請(qǐng)求,或者多個(gè)并行的不同數(shù)據(jù)請(qǐng)求。我們可以通過給請(qǐng)求配置添加一個(gè)自定義標(biāo)記(如 allowRepeat: true)來實(shí)現(xiàn)白名單。

// 在攔截器中
service.interceptors.request.use(
? (config) =>?{
? ? // 如果配置了允許重復(fù),則跳過取消邏輯
? ? if?(config.allowRepeat) {
? ? ? return?config;
? ? }
? ? removePendingRequest(config);
? ? addPendingRequest(config);
? ? return?config;
? }
);

// 在組件中使用
request.get('/api/polling', {?allowRepeat:?true?});

2. 與 Vue Router 導(dǎo)航守衛(wèi)結(jié)合

當(dāng)用戶切換頁面時(shí),我們通常希望取消所有未完成的請(qǐng)求,以免它們?cè)诤笈_(tái)繼續(xù)運(yùn)行并可能更新一個(gè)已經(jīng)銷毀的組件狀態(tài)。這可以在Vue Router的全局前置守衛(wèi)中實(shí)現(xiàn)。

// router/index.js
import?router?from?'./router';
import?{ pendingRequestMap }?from?'@/utils/request-abort';?// 需要將map導(dǎo)出

router.beforeEach((to,?from, next) =>?{
? // 遍歷并中止所有進(jìn)行中的請(qǐng)求
? pendingRequestMap.forEach((controller, key) =>?{
? ? controller.abort(`路由跳轉(zhuǎn)至?${to.path}`);
? });
? // 清空Map
? pendingRequestMap.clear();
? next();
});

3. 使用 Composition API 封裝

在Vue3中,我們可以利用組合式API,將取消邏輯封裝成一個(gè)更優(yōu)雅、可復(fù)用的 useRequest Hook。

// composables/useRequest.js
import?{ ref, onUnmounted }?from?'vue';
import?request?from?'@/utils/request-abort';

export?function?useRequest() {
? const?data =?ref(null);
? const?error =?ref(null);
? const?loading =?ref(false);
? let?abortController =?null;

? const?execute?=?async?(config) => {
? ? loading.value?=?true;
? ? error.value?=?null;
? ? // 為這次執(zhí)行創(chuàng)建一個(gè)獨(dú)立的控制器(可選,如果全局已管理則可省略)
? ? abortController =?new?AbortController();
? ? try?{
? ? ? const?response =?await?request({
? ? ? ? ...config,
? ? ? ? signal: abortController.signal
? ? ? });
? ? ? data.value?= response.data;
? ? ? return?response;
? ? }?catch?(err) {
? ? ? if?(!axios.isCancel(err)) {
? ? ? ? error.value?= err;
? ? ? }
? ? ? throw?err;
? ? }?finally?{
? ? ? loading.value?=?false;
? ? }
? };

? // 組件卸載時(shí),取消由這個(gè)Hook發(fā)起的請(qǐng)求
? onUnmounted(() =>?{
? ? if?(abortController) {
? ? ? abortController.abort();
? ? }
? });

? // 提供一個(gè)手動(dòng)取消的方法
? const?cancel?= () => {
? ? if?(abortController) {
? ? ? abortController.abort();
? ? }
? };

? return?{
? ? data,
? ? error,
? ? loading,
? ? execute,
? ? cancel
? };
}

在組件中使用:

<script setup>
import?{ useRequest }?from?'@/composables/useRequest';

const?{ data, loading, execute } =?useRequest();

const?handleSubmit?= () => {
? execute({
? ? method:?'post',
? ? url:?'/api/submit',
? ? data: {?/* ... */?}
? });
};
</script>

取消重復(fù)請(qǐng)求是一個(gè)看似簡單,但能顯著提升應(yīng)用健壯性的優(yōu)化點(diǎn)。在Vue3項(xiàng)目中,結(jié)合 axios 和 AbortController,我們可以用清晰的代碼實(shí)現(xiàn)這一功能。希望本文介紹的方法和思路,能幫助你更好地處理項(xiàng)目中的網(wǎng)絡(luò)請(qǐng)求問題。

以上就是在Vue3項(xiàng)目中取消重復(fù)請(qǐng)求的兩種方案的詳細(xì)內(nèi)容,更多關(guān)于Vue3取消重復(fù)請(qǐng)求方案的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue自定義表單內(nèi)容檢查rules實(shí)例

    Vue自定義表單內(nèi)容檢查rules實(shí)例

    這篇文章主要介紹了Vue自定義表單內(nèi)容檢查rules實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • 如何封裝了一個(gè)vue移動(dòng)端下拉加載下一頁數(shù)據(jù)的組件

    如何封裝了一個(gè)vue移動(dòng)端下拉加載下一頁數(shù)據(jù)的組件

    這篇文章主要介紹了如何封裝了一個(gè)vue移動(dòng)端下拉加載下一頁數(shù)據(jù)的組件,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • Vue項(xiàng)目中使用addRoutes出現(xiàn)問題的解決方法

    Vue項(xiàng)目中使用addRoutes出現(xiàn)問題的解決方法

    大家應(yīng)該都知道可以通過vue-router官方提供的一個(gè)api-->addRoutes可以實(shí)現(xiàn)路由添加的功能,事實(shí)上就也就實(shí)現(xiàn)了用戶權(quán)限,這篇文章主要給大家介紹了關(guān)于Vue項(xiàng)目中使用addRoutes出現(xiàn)問題的解決方法,需要的朋友可以參考下
    2021-08-08
  • 用Vue寫一個(gè)分頁器的示例代碼

    用Vue寫一個(gè)分頁器的示例代碼

    本篇文章主要介紹了用Vue寫一個(gè)分頁器的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-04-04
  • Vue腳手架配置代理服務(wù)器的兩種方式(小結(jié))

    Vue腳手架配置代理服務(wù)器的兩種方式(小結(jié))

    本文主要介紹了Vue腳手架配置代理服務(wù)器的兩種方式(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 詳解Vue用自定義指令完成一個(gè)下拉菜單(select組件)

    詳解Vue用自定義指令完成一個(gè)下拉菜單(select組件)

    本篇文章主要介紹了詳解Vue用自定義指令完成一個(gè)下拉菜單(select組件),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • vue項(xiàng)目搭建以及全家桶的使用詳細(xì)教程(小結(jié))

    vue項(xiàng)目搭建以及全家桶的使用詳細(xì)教程(小結(jié))

    這篇文章主要介紹了vue項(xiàng)目搭建以及全家桶的使用詳細(xì)教程(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-12-12
  • 深入了解Vue3中props的原理與使用

    深入了解Vue3中props的原理與使用

    props指父組件往子組件中傳入?yún)?shù),這篇文章主要為大家介紹了vue3中props的原理與使用,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2023-05-05
  • vue3動(dòng)態(tài)加載對(duì)話框的方法實(shí)例

    vue3動(dòng)態(tài)加載對(duì)話框的方法實(shí)例

    對(duì)話框是很常用的組件,在很多地方都會(huì)用到,下面這篇文章主要給大家介紹了關(guān)于vue3動(dòng)態(tài)加載對(duì)話框的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • 詳解vue3?defineModel如何實(shí)現(xiàn)雙向綁定

    詳解vue3?defineModel如何實(shí)現(xiàn)雙向綁定

    隨著?Vue?3.3?引入的?defineModel?宏,開發(fā)者可以更加簡潔地實(shí)現(xiàn)組件內(nèi)部的雙向數(shù)據(jù)綁定,下面就跟隨小編一起來學(xué)習(xí)一下如何使用defineModel實(shí)現(xiàn)雙向綁定吧
    2024-12-12

最新評(píng)論

孟村| 镇安县| 文昌市| 拜泉县| 三都| 上高县| 盈江县| 双江| 昌黎县| 务川| 玉屏| 区。| 攀枝花市| 色达县| 米易县| 宁明县| 砚山县| 兰州市| 上思县| 东海县| 东乡族自治县| 昔阳县| 马尔康县| 新化县| 文登市| 盘山县| 宜兰县| 黎川县| 青龙| 星子县| 隆尧县| 汝城县| 博兴县| 尖扎县| 东港市| 荆州市| 石景山区| 吴堡县| 樟树市| 石泉县| 锡林郭勒盟|