切換選單後,Vue畫面上的選項已變,載入內容卻停在第一次?如果你把資料讀取寫在watchEffect(async () => ...)裡,先看那個欄位是在第一個await之前還是之後讀取。watchEffect會自動追蹤同步執行時讀到的響應式資料;非同步函式恢復後才讀到的值,不會因此成為它的依賴。
先做一個沒有網路請求的小例子
下面用Vue 3的Composition API。把程式放到元件的<script setup>,開啟瀏覽器Console;這裡的Promise.resolve()只用來跨過非同步界線,沒有呼叫外部服務。
import { ref, watchEffect, nextTick } from 'vue'
const section = ref('首頁')
watchEffect(async () => {
await Promise.resolve()
console.log('讀取內容:', section.value)
})
// 等第一次回呼完成後,再模擬使用者切換。
async function testSwitch() {
await nextTick()
section.value = '商品頁'
await nextTick()
}
void testSwitch()
這段只會印出一次「讀取內容:首頁」。雖然section.value確實改為「商品頁」,回呼內對它的第一次讀取已在await之後,因此這個watchEffect沒有追蹤它。不要只因為函式裡出現了欄位名稱,就認定它已被監看。
把需要追蹤的值先讀出來
先在同步部分取得值,再把這次的值交給後續工作。同一個元件測試時,請用下面整段替換上面的例子,避免同時建立兩個回呼。
import { ref, watchEffect, nextTick } from 'vue'
const section = ref('首頁')
watchEffect(async () => {
const selectedSection = section.value
await Promise.resolve()
console.log('讀取內容:', selectedSection)
})
async function testSwitch() {
await nextTick()
section.value = '商品頁'
await nextTick()
}
void testSwitch()
現在會依次印出「首頁」和「商品頁」。第一行section.value在await之前讀取,Vue便能建立依賴。selectedSection同時保留這一輪的輸入,後續工作不必再回頭拿可能已改變的值。
真正發送請求時,若網址在await fetch(...)的參數中已讀取section.value,那次讀取發生在等待之前,仍可被追蹤。判斷重點是執行順序,不能把任何帶await的程式都當成失效。
只想監看一個欄位,用watch更直接
當「哪個值改了要重新載入」很明確,用watch直接列出來源,便不用依靠回呼裡的自動追蹤。以下同樣是可替換的完整小例子。
import { ref, watch, nextTick } from 'vue'
const section = ref('首頁')
watch(section, async (selectedSection) => {
await Promise.resolve()
console.log('讀取內容:', selectedSection)
}, { immediate: true })
async function testSwitch() {
await nextTick()
section.value = '商品頁'
await nextTick()
}
void testSwitch()
section是明列的監看來源,回呼中的await不會把它從來源移除。immediate: true讓第一次也執行;省略這個選項,普通watch會等來源變動才執行。若來源是響應式物件的一個欄位,可傳入getter,例如watch(() => filters.section, ...),不要直接傳入當下的字串值。
重新執行和取消舊工作是兩件事
把讀取位置調整好,只解決「改值後有沒有再執行」。如果使用者連續切換,前一次請求可能仍在進行,最後才回來覆蓋較新的結果。那需要另外處理取消、清理或結果序號;自動追蹤依賴不會自動替你取消網路請求。
若只需要由資料計算出另一個值,沒有請求、儲存或其他副作用,通常先考慮computed。不要為了讓畫面更新而多加一個互相寫入資料的watcher。
回到原本程式時怎麼查
從watchEffect回呼的開頭往下找第一個await,列出它之前實際讀取的響應式值。檢查被呼叫的函式時也要看執行時機:同步讀取可以被追蹤,延後到非同步回呼才讀取則不同。需要重新載入的欄位若全部在等待之後才取值,先提早取得,或改用明列來源的watch。
測試時先確認初始回呼已完成,再一次改一個值,看執行紀錄與輸入是否符合預期。不要把同一輪排程內的多次改值,預設成每次都會有一筆回呼;Vue會對一般watcher回呼進行批次處理。

評論0