深入理解 Vue 3 中的 watchEffect
:执行时机、陷阱与性能优化
一、watchEffect
是什么?
在 Vue 3 中,watchEffect
是组合式 API 中用于自动收集依赖并响应变化的副作用函数 。它在响应式依赖变动时重新运行,类似于 computed
的"副作用版"。
相比传统的 watch
,它更自动、代码更简洁。
二、基本用法回顾
javascript
import { ref, watchEffect } from 'vue'
const count = ref(0)
watchEffect(() => {
console.log('count is:', count.value)
})
这段代码会:
- 立即执行一次
- 依赖收集 count
- 当 count 变化时自动重新运行
三、关键难点:执行时机 & 执行机制
执行时机:
默认情况下,watchEffect
是在组件渲染前同步执行,但也可以通过选项控制:
arduino
watchEffect(() => {
console.log('DOM 渲染前执行')
}, {
flush: 'pre' // 默认值
})
其他选项:
flush 类型 | 执行时机 | 使用场景 |
---|---|---|
'pre' |
渲染前(默认) | 需要在 DOM 更新前读取状态 |
'post' |
渲染后(DOM 更新后) | 依赖 DOM 或使用 $el |
'sync' |
立即同步执行 | 依赖顺序执行或数据推送场景 |
scss
watchEffect(() => {
console.log(el.value?.offsetHeight)
}, {
flush: 'post'
})
四、常见陷阱
1. 非响应式变量不会触发重新执行
javascript
let name = 'Tom'
watchEffect(() => {
console.log(name) // 这不是响应式变量
})
解决方法:使用 ref
或 reactive
2. 非主动使用 .value
的响应式变量不会被追踪
scss
const user = ref({ name: 'Tom' })
watchEffect(() => {
console.log(user.value) // 会触发
// console.log(user.value.name) // 也会触发
// 但如果只访问 user 而不访问 .value,无法追踪
})
3. 副作用函数里有异步操作要清理
javascript
watchEffect((onCleanup) => {
const timer = setInterval(() => {
console.log('轮询')
}, 1000)
onCleanup(() => {
clearInterval(timer)
})
})
如果不清理副作用,组件卸载或数据切换时可能导致内存泄漏或逻辑冲突。
五、性能优化建议
1.避免在 watchEffect
内执行复杂逻辑
scss
watchEffect(() => {
// 慎用:大量计算、循环、DOM 查询
for (let i = 0; i < 10000; i++) { ... }
})
➡ 拆分逻辑 或改用 watch
+ debounce 更合适
2. 需要精确控制依赖时,优先使用 watch
javascript
watch(() => someReactive.value, (newVal) => {
// 更精细的控制
})
六、实践 Demo:表单自动保存
xml
<script setup>
import { ref, watchEffect } from 'vue'
const form = ref({ title: '', content: '' })
watchEffect(() => {
if (form.value.title || form.value.content) {
localStorage.setItem('draft', JSON.stringify(form.value))
}
})
</script>
<template>
<input v-model="form.title" placeholder="标题" />
<textarea v-model="form.content" placeholder="内容" />
</template>
自动保存草稿,用户刷新不会丢失。
七、总结:何时使用 watchEffect
?
使用时机 | 推荐工具 |
---|---|
自动依赖追踪、副作用立即执行 | watchEffect |
精确监听某个属性并节流或控制频率 | watch |
不需要副作用,只计算依赖结果 | computed |
八、源码解析拓展(进阶)
在 Vue 3 中,watchEffect
实际通过 effect()
实现响应式副作用收集,并通过 scheduler
控制重新执行时机。
源码文件位置:packages/reactivity/src/effect.ts
核心关键代码(简化版):
javascript
function watchEffect(fn, options) {
const runner = effect(fn, {
scheduler: queueJob, // 控制刷新节奏
...options
})
}
总结:
watchEffect
是 Composition API 中最强大的响应式工具之一,但也最容易被误用。理解它的"立即执行"、"依赖追踪"和"清理机制",才能用好它!