Vue3 中 watch的 flush 選項(默認(rèn)無/`post`/`sync`)的區(qū)別全解析
一、先搞懂核心概念:flush 控制「監(jiān)聽回調(diào)的執(zhí)行時機(jī)」
flush 是 watch 的可選配置項,決定當(dāng)監(jiān)聽的數(shù)據(jù)變化后,回調(diào)函數(shù)什么時候執(zhí)行。Vue3 中 DOM 更新是異步的,這是理解 flush 的關(guān)鍵前提:
- Vue 會把同一輪事件循環(huán)中的所有數(shù)據(jù)變更收集起來,異步批量更新 DOM(避免頻繁操作 DOM 影響性能);
flush就是控制 watch 回調(diào)在「DOM 更新前」「DOM 更新后」還是「同步」執(zhí)行。
二、三種 flush 模式的核心區(qū)別(代碼示例對比)
先定義基礎(chǔ)模板和數(shù)據(jù),后續(xù)只改 flush 配置:
<template>
<!-- 綁定到 DOM 的數(shù)據(jù) -->
<div id="content">{{ count }}</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
// 點擊按鈕修改 count
const add = () => {
count.value++
console.log('點擊后立即打?。?, document.getElementById('content')?.innerText)
}
</script>1. 默認(rèn)模式(不寫 flush)→flush: 'pre'(Vue3 隱式默認(rèn)值)
- 執(zhí)行時機(jī):數(shù)據(jù)變化后 → DOM 更新前 執(zhí)行 watch 回調(diào);
- 核心特點:回調(diào)里拿不到「更新后的 DOM」,因為 DOM 還沒刷新。
代碼示例:
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
// 默認(rèn) flush: 'pre'(不寫就是這個)
watch(count, (newVal) => {
console.log('watch 回調(diào):', document.getElementById('content')?.innerText)
console.log('新值:', newVal)
})
const add = () => {
count.value++
console.log('點擊后立即打?。?, document.getElementById('content')?.innerText)
}
</script>執(zhí)行結(jié)果(點擊按鈕后):
點擊后立即打?。? // DOM 還沒更
watch 回調(diào):0 // watch 回調(diào)執(zhí)行(DOM 仍未更)
[Vue 異步更新 DOM] // 此時 Vue 才更新 DOM
2. flush: ‘post’ → DOM 更新后執(zhí)行
- 執(zhí)行時機(jī):數(shù)據(jù)變化后 → Vue 異步更新 DOM → DOM 更新完成后 執(zhí)行 watch 回調(diào);
- 核心特點:回調(diào)里能拿到「更新后的 DOM」,這是最常用的場景(比如監(jiān)聽數(shù)據(jù)后操作 DOM)。
代碼示例:
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
// 顯式指定 flush: 'post'
watch(count, (newVal) => {
console.log('watch 回調(diào):', document.getElementById('content')?.innerText)
console.log('新值:', newVal)
}, { flush: 'post' }) // 關(guān)鍵配置
const add = () => {
count.value++
console.log('點擊后立即打印:', document.getElementById('content')?.innerText)
}
</script>執(zhí)行結(jié)果(點擊按鈕后):
點擊后立即打?。? // DOM 還沒更
[Vue 異步更新 DOM] // Vue 先更 DOM
watch 回調(diào):1 // watch 回調(diào)執(zhí)行(能拿到更新后的 DOM)
3. flush: ‘sync’ → 同步執(zhí)行
- 執(zhí)行時機(jī):數(shù)據(jù)變化后 → 立即同步執(zhí)行 watch 回調(diào)(不等待 DOM 更新,也不等待批量更新);
- 核心特點:數(shù)據(jù)變了回調(diào)就執(zhí)行,同步阻塞代碼,但性能差(會打破 Vue 的異步批量更新優(yōu)化),盡量少用。
代碼示例:
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
// 顯式指定 flush: 'sync'
watch(count, (newVal) => {
console.log('watch 回調(diào):', document.getElementById('content')?.innerText)
console.log('新值:', newVal)
}, { flush: 'sync' }) // 關(guān)鍵配置
const add = () => {
count.value++
console.log('點擊后立即打?。?, document.getElementById('content')?.innerText)
}
</script>執(zhí)行結(jié)果(點擊按鈕后):
watch 回調(diào):0 // 數(shù)據(jù)一變,回調(diào)立即執(zhí)行(DOM 還沒更)
點擊后立即打印:0 // 后續(xù)代碼執(zhí)行
[Vue 異步更新 DOM] // 最后 Vue 更新 DOM
三、三種模式的適用場景(新手速記)
| flush 模式 | 執(zhí)行時機(jī) | 核心用途 | 注意事項 |
|---|---|---|---|
| 默認(rèn)(pre) | DOM 更新前 | 數(shù)據(jù)校驗、提前修改數(shù)據(jù)(不依賴 DOM) | 拿不到更新后的 DOM |
| post | DOM 更新后 | 操作 DOM、獲取更新后的元素尺寸/位置 | 最常用,無性能問題 |
| sync | 數(shù)據(jù)變化同步 | 必須立即響應(yīng)數(shù)據(jù)變化(極少場景) | 性能差,打破批量更新優(yōu)化 |
典型場景舉例:
用 post 的場景(最常用):
監(jiān)聽列表數(shù)據(jù)變化后,重新初始化第三方組件(比如表格、圖表),需要基于更新后的 DOM 渲染:
<script setup>
import { ref, watch } from 'vue'
import ECharts from 'echarts'
const dataList = ref([1,2,3])
const chartRef = ref(null)
// 數(shù)據(jù)變化后,基于更新后的 DOM 重新渲染圖表
watch(dataList, () => {
const chart = ECharts.init(chartRef.value)
chart.setOption({ series: [{ data: dataList.value }] })
}, { flush: 'post' }) // 必須等 DOM 更新后才能拿到 chartRef
</script>用 sync 的場景(極少用):
只有當(dāng)你需要「數(shù)據(jù)一變,立即觸發(fā)回調(diào),且回調(diào)結(jié)果會影響后續(xù)同步代碼」時才用:
<script setup>
import { ref, watch } from 'vue'
const flag = ref(false)
let result = ''
watch(flag, (newVal) => {
result = newVal ? '開啟' : '關(guān)閉'
}, { flush: 'sync' })
const test = () => {
flag.value = true
console.log(result) // 用 sync 會打印「開啟」,默認(rèn)/pre 會打印空字符串
}
</script>用默認(rèn) pre 的場景:
數(shù)據(jù)校驗(比如輸入框內(nèi)容長度限制),不需要依賴 DOM:
<script setup>
import { ref, watch } from 'vue'
const inputVal = ref('')
// 數(shù)據(jù)變化后,DOM 更新前校驗并修正數(shù)據(jù)
watch(inputVal, (newVal) => {
if (newVal.length > 10) {
inputVal.value = newVal.slice(0, 10) // 截斷超長內(nèi)容
}
}) // 默認(rèn) pre 即可,無需等 DOM 更新
</script>四、進(jìn)階補(bǔ)充:watchPostEffect(語法糖)
Vue3 提供了 watchPostEffect,等價于 watch(..., { flush: 'post' }),寫法更簡潔:
<script setup>
import { ref, watchPostEffect } from 'vue'
const count = ref(0)
// 等價于 watch(count, () => {}, { flush: 'post' })
watchPostEffect(() => {
console.log('DOM 更新后執(zhí)行:', document.getElementById('content').innerText)
})
</script>同理,watchSyncEffect 是 flush: 'sync' 的語法糖,watchEffect 是默認(rèn) pre 的語法糖。

總結(jié)
- 核心區(qū)別:
flush控制 watch 回調(diào)在「DOM 更新前(pre)」「DOM 更新后(post)」還是「同步(sync)」執(zhí)行; - 常用選擇:90% 場景用
flush: 'post'(或watchPostEffect),需要操作 DOM 必選; - 性能提醒:
sync盡量不用,會失去 Vue 異步批量更新的性能優(yōu)化。
到此這篇關(guān)于Vue3 中 watch的 flush 選項(默認(rèn)無/`post`/`sync`)的區(qū)別全解析的文章就介紹到這了,更多相關(guān)vue watch的 flush 選項內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談一下Vue生命周期中mounted和created的區(qū)別
每一個vue實例從創(chuàng)建到銷毀的過程,就是這個vue實例的生命周期,在這個過程中,他經(jīng)歷了從開始創(chuàng)建、初始化數(shù)據(jù)、編譯模板、掛載Dom、渲染→更新→渲染、卸載等一系列過程,那么這些過程中,具體vue做了些啥,我們今天來了解一下2023-05-05
vue?跳轉(zhuǎn)頁面$router.resolve和$router.push案例詳解
這篇文章主要介紹了vue?跳轉(zhuǎn)頁面$router.resolve和$router.push案例詳解,這樣實現(xiàn)了既跳轉(zhuǎn)了新頁面,又不會讓后端檢測到頁面鏈接不安全之類的,需要的朋友可以參考下2023-10-10

