931 字
5 分钟
Vue 3 的 watchEffect

watchEffect 是什么#

watchEffect 接收一个函数,立即执行一次,依赖变化后重新执行。它不用写监听源:回调里读到哪个响应式数据,就追踪哪个。

收集依赖只发生在同步执行期间,读到哪个数据就追踪哪个。没读到的数据变化时,不会触发回调。

const message = ref('')
const message2 = ref('')
watchEffect(() => {
console.log(message2.value)
})

这段只读了 message2,所以 message2 变化时重跑,message 怎么变都不触发。

重新执行时依赖会重新收集:上一次读到、这一次没读到的数据,之后变化不会再触发回调。

清除副作用:onCleanup#

回调的第一个参数是 onCleanup,它接收一个清理函数:

type OnCleanup = (cleanupFn: () => void) => void

清理函数不在本次执行里运行,留到之后调用。时机有三个:下一次重新执行之前、手动停止监听之后、组件卸载时。

一次执行的过程是:先跑上一次注册的清理函数,再执行新的回调,再注册新的清理函数。

onCleanup 这个参数名从 Vue 3.2.31 开始使用。

用清理函数做防抖#

输入框每变一次就发一次请求,连续输入会发出一串请求。没有清理的写法里,每次重跑都会新建一个定时器,之前的定时器不会被取消,连续输入就会发出多个请求。

把定时器交给 onCleanup,只保留最后一次:

const keyword = ref('')
watchEffect((onCleanup) => {
const value = keyword.value
const timer = setTimeout(() => {
console.log('发送请求', value)
}, 500)
onCleanup(() => {
clearTimeout(timer)
})
})

输入停止 500 ms 后才发一次请求,这是 onCleanup 最常用的场景。

取消过期请求#

新的请求发出后,上一个请求的结果已经没有用。清理函数里可以调用 AbortController.abort() 把它取消。

watchEffect(async (onCleanup) => {
const controller = new AbortController()
onCleanup(() => {
controller.abort()
})
const res = await fetch(`/api/search?q=${keyword.value}`, {
signal: controller.signal
})
})

keyword.value 在 await 之前读到,依赖照常建立。请求被取消时 fetch 会抛 AbortError。

移除事件监听#

每次重跑都挂一次监听,不清理就会挂上多个 handler。移除时要用同一个函数引用,所以把 handler 写成具名函数。

watchEffect((onCleanup) => {
const text = keyword.value
function onScroll() {
console.log(text)
}
window.addEventListener('scroll', onScroll)
onCleanup(() => {
window.removeEventListener('scroll', onScroll)
})
})

停止与暂停监听#

watchEffect 返回一个 WatchHandle,用它控制这次监听。停止之后,依赖再变化也不会重跑。

const handle = watchEffect(() => {
console.log(message.value)
})
handle.pause() // 暂停:依赖变化不再触发
handle.resume() // 恢复
handle.stop() // 停止:不能再恢复

WatchHandle 本身可以直接调用,等同于 stop(),所以 const stop = watchEffect(fn) 之后调用 stop() 的旧写法依然有效。

flush 执行时机#

flush 决定回调在哪个阶段执行,三个可选值,默认是 pre。

flush执行时机
pre默认值,组件更新前执行
post组件更新后执行,DOM 已更新
sync依赖一变就同步执行

需要读写更新后的 DOM 时用 post:

watchEffect(() => {
// 这里能拿到更新后的 DOM
}, { flush: 'post' })

onTrigger 与 onTrack#

这两个选项只在开发模式下生效,用来观察依赖什么时候被读取、什么时候触发重跑。

watchEffect(() => {
console.log(message.value)
}, {
onTrack(event) {
console.log('追踪到依赖', event)
},
onTrigger(event) {
console.log('触发重跑', event)
}
})

第 9 篇,共 19 篇本文属于系列

Vue 3 的 watchEffect
https://www.choria.top/posts/vue3-watch-effect/
作者
乔瑞雅·克林威尔
发布于
2026-04-08
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

最后更新于 ,距今已过 214 天

部分内容可能已过时