欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3 + vue-query 的重復請求問題解決

 更新時間:2025年09月09日 11:02:06   作者:CassieHuu  
在最近一次開發(fā)中就遇到了重復請求的問題,本文就來介紹一下Vue3 + vue-query 的重復請求問題解決,具有一定的參考價值,感興趣的可以了解一下

前言

@tanstack/vue-query 以其強大的緩存和狀態(tài)管理能力,極大地簡化了數(shù)據(jù)獲取邏輯。然而,當它與 Vue3 復雜的響應式系統(tǒng)結合時,如果不深入理解其工作原理,很容易陷入“重復請求”的陷阱。在最近一次開發(fā)中就遇到了這個問題,本文展現(xiàn)了從一個頁面加載時觸發(fā)4次重復API請求,到2次,并最終實現(xiàn)1次請求的解決過程,以免下次再掉進這個坑里。

問題的初現(xiàn):一個“勤奮過頭”的列表頁

我們有一個需求:開發(fā)一個包含Tabs切換、獨立篩選條件和分頁功能的數(shù)據(jù)列表頁面。技術棧是 Vue 3 (Composition API) 和 @tanstack/vue-query,并使用了一個名為 <cec-page-wrapper> 的高度封裝的表格組件。

頁面完成后,一切似乎工作正常,但打開瀏覽器網(wǎng)絡面板,我們驚恐地發(fā)現(xiàn),每次加載頁面,獲取列表數(shù)據(jù)的API transactionList 竟然被調用了4次!

發(fā)生了什么???

第一階段:從4次請求到2次 —— 尋找“幽靈觸發(fā)者”

我們首先審查了代碼,試圖找出所有可能調用 loadData (即 useQueryrefetch) 的地方。

當時的 script setup 核心邏輯:

// ...
const activeTab = ref('PROVIDER');
const tablePage = ref({ currentPage: 1, ... });
const filterParams = ref({ ... });

const { isFetching, data: tableData, refetch: loadData } = useQuery({
  queryKey: ['transactionList', activeTab, tablePage, filterParams],
  queryFn: async () => { /* ... */ },
});

watch(activeTab, () => {
  tablePage.value.currentPage = 1;
  loadData(); // Tab 切換時加載
});

onMounted(() => {
  getBusinessNodesList(); // 獲取下拉框選項
  loadData(); // 掛載時加載
});

// 模板中,分頁組件 @change 事件也調用了 loadData

經(jīng)過仔細的日志打印和分析,我們定位到了這4次請求的來源:

  1. 第一次 (useQuery 自動觸發(fā)): useQuery 在組件掛載時,會使用初始的 queryKey 自動發(fā)起一次請求。
  2. 第二次 (onMounted 手動觸發(fā)): onMounted 鉤子中明確調用了 loadData()。
  3. 第三次 (watch(activeTab)): activeTab 在初始化時,watch 監(jiān)聽器被觸發(fā),調用了 loadData()。
  4. 第四次 (PageWrapper 初始化): 封裝的 <cec-page-wrapper> 組件在內部的分頁器初始化時,會 emit 一次 @load-data 事件,再次調用了 loadData()。

根源: 我們犯了一個典型的錯誤——不信任框架,手動控制一切。我們試圖在每個可能的地方都調用 loadData,卻沒有意識到 useQuery 的響應式 queryKey 已經(jīng)為我們處理了大部分情況,從而導致了多次重復的調用。

解決方案 (V1): 我們決定信任 useQuery,并精確控制請求時機。

  1. 禁用自動請求: 給 useQuery 添加 enabled: false 選項。
  2. 添加 isMounted 守衛(wèi): 創(chuàng)建一個 isMounted 標志位,阻止分頁組件在 onMounted 完成前的初始化調用。
  3. 統(tǒng)一入口: 在 onMounted 的最后,手動調用 refetch() 來發(fā)起唯一的第一次請求。
// ...
const { ..., refetch } = useQuery({ ..., enabled: false });
const isMounted = ref(false);

const handlePageChange = (pageInfo) => {
  if (!isMounted.value) return; // 守衛(wèi)
  // ...
  refetch();
};

onMounted(async () => {
  await getBusinessNodesList();
  searchParams.value = cloneDeep(activeFilters.value);
  await refetch(); // 手動觸發(fā)
  isMounted.value = true;
});

結果: 這個修改非常有效!請求次數(shù)從4次銳減到了2次。但為什么還有2次?

第二階段:從2次請求到1次 —— 深入響應式依賴

我們再次審查代碼,發(fā)現(xiàn) onMounted 中手動調用的 refetch() 仍然是多余的。盡管我們禁用了它,但 watch 和其他地方的邏輯仍然可能在初始化階段觸發(fā) refetch。

更深層次的根源: useQuery 的 queryKey 依賴于 searchParams。而 searchParams 的初始值是通過 cloneDeep(activeFilters.value) 計算得來的。activeFilters 又依賴 filterParams 和 activeTab。整個依賴鏈條是這樣的:

activeTab -> activeFilters -> searchParams -> queryKey

在組件掛載的微任務隊列中,這些響應式數(shù)據(jù)的初始化和 watch 的觸發(fā)順序存在我們未預料到的交互,導致了 searchParams 在短時間內被更新了兩次。

最終的解決方案 (V2): 我們意識到,問題的關鍵不是去“堵”住所有的觸發(fā)點,而是讓觸發(fā)機制變得單一和可預測。

  1. 回歸 useQuery 的自動行為: 我們移除 enabled: false,我們相信并利用它的自動加載能力。
  2. 確保初始 queryKey 的穩(wěn)定性: 最重要的修改——在定義 searchParams 時,就立即用穩(wěn)定的初始值 cloneDeep(activeFilters.value) 對其進行初始化。
  3. 移除所有手動的首次加載調用: 刪除 onMounted 中的 refetch() 或 search() 調用。

最終只有1次請求的代碼:

// 1. 狀態(tài)定義:searchParams 在定義時就擁有了正確的初始值
const activeTab = ref('PROVIDER');
const activeFilters = computed(() => filterParams[activeTab.value]);
const searchParams = ref(cloneDeep(activeFilters.value)); // 關鍵!

// 2. useQuery: 默認啟用,它會在掛載時自動使用上面的 searchParams 發(fā)起請求
const { isFetching, data: tableData, refetch } = useQuery({
  queryKey: computed(() => ['transactionList', activeTab.value, ..., searchParams.value]),
  queryFn: async () => { /* ... */ },
});

// 3. 事件處理:只負責更新狀態(tài),不直接調用 refetch
const search = () => {
  activeTableState.value.currentPage = 1;
  // 只更新 searchParams,useQuery 會自動響應 queryKey 的變化
  searchParams.value = cloneDeep(activeFilters.value); 
};

// 4. 生命周期:只做與列表數(shù)據(jù)無關的初始化
onMounted(() => {
  getBusinessNodesList();
  // 不需要任何 loadData 或 refetch 調用!
});

為什么這次能成功?

  • 單一入口: useQuery 的 queryFn 現(xiàn)在是獲取數(shù)據(jù)的唯一入口。
  • 單一觸發(fā)器: queryKey 的變化是觸發(fā) queryFn 的唯一方式。
  • 可預測的狀態(tài): 在組件掛載時,searchParams 的初始值是確定的、穩(wěn)定的。useQuery 使用這個穩(wěn)定的 key 發(fā)起了唯一一次初始請求。
  • 清晰的職責:
    • 用戶的輸入只改變 activeFilters (UI狀態(tài))。
    • 用戶的搜索動作 (@search, @change, reset) 才去調用 search(),將 UI 狀態(tài)“提交”給 searchParams (查詢狀態(tài))。
    • useQuery 忠實地響應 searchParams 的變化。

結論

還是要深入了解 Vue 3 響應式系統(tǒng)和 vue-query 聲明式數(shù)據(jù)獲取的理念啊~~~不然有AI也要卡殼子,耽誤牛馬下班~

  • 不要與框架對抗: 相信 useQuery 的響應式能力,避免在 onMountedwatch 中進行手動的、命令式的 refetch 調用。
  • 分離狀態(tài): 將用于UI雙向綁定的狀態(tài)(如 activeFilters)和用于觸發(fā)數(shù)據(jù)請求的狀態(tài)(如 searchParams)分離開,可以有效切斷意外的響應式連鎖反應。
  • 穩(wěn)定 queryKey: 確保 useQuery 在首次自動執(zhí)行時,其 queryKey 所依賴的所有數(shù)據(jù)都已處于穩(wěn)定和正確的初始狀態(tài)。

通過遵循這些原則,我們可以構建出既簡潔又健壯的數(shù)據(jù)獲取邏輯,真正發(fā)揮出現(xiàn)代前端框架的威力。

到此這篇關于Vue3 + vue-query 的重復請求問題解決 的文章就介紹到這了,更多相關vue-query 重復請求內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論