watch 与 watchEffect 的区别
Vue 3 中两种侦听器的使用场景和差异
问题
Vue 3 提供了 watch 和 watchEffect 两种侦听器,它们有什么区别?分别在什么场景下使用?
解答
watchEffect
自动收集依赖,立即执行:
import { ref, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Tom')
// 自动追踪回调中用到的响应式数据
watchEffect(() => {
// 会自动收集 count 和 name 作为依赖
console.log(`count: ${count.value}, name: ${name.value}`)
})
// 修改任一依赖都会触发回调
count.value++ // 触发
name.value = 'Jerry' // 触发
watch
显式指定依赖,默认懒执行:
import { ref, watch } from 'vue'
const count = ref(0)
// 侦听单个 ref
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} -> ${newVal}`)
})
// 侦听多个数据源
const name = ref('Tom')
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}`)
console.log(`name: ${oldName} -> ${newName}`)
})
// 侦听 getter 函数
const obj = ref({ a: 1 })
watch(
() => obj.value.a,
(newVal) => {
console.log(`obj.a: ${newVal}`)
}
)
侦听对象的区别
import { reactive, watch, watchEffect } from 'vue'
const state = reactive({ user: { name: 'Tom', age: 18 } })
// watchEffect: 只追踪回调中实际访问的属性
watchEffect(() => {
console.log(state.user.name) // 只有 name 变化才触发
})
// watch: 需要配置 deep 才能侦听嵌套变化
watch(
() => state.user,
(newVal) => {
console.log(newVal)
},
{ deep: true } // 深度侦听
)
获取旧值
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
// watch 可以获取旧值
watch(count, (newVal, oldVal) => {
console.log(`${oldVal} -> ${newVal}`) // 0 -> 1
})
// watchEffect 无法获取旧值
watchEffect(() => {
console.log(count.value) // 只能拿到当前值
})
count.value = 1
执行时机
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
// watchEffect 立即执行
watchEffect(() => {
console.log('watchEffect:', count.value) // 立即打印 0
})
// watch 默认懒执行
watch(count, (val) => {
console.log('watch:', val) // 不会立即执行
})
// watch 配置 immediate 可立即执行
watch(
count,
(val) => {
console.log('watch immediate:', val) // 立即打印 0
},
{ immediate: true }
)
关键点
- 依赖收集:
watchEffect自动收集,watch显式指定 - 执行时机:
watchEffect立即执行,watch默认懒执行 - 旧值访问:
watch可获取新旧值,watchEffect只能获取当前值 - 使用场景:
watchEffect适合副作用同步,watch适合需要对比新旧值或精确控制侦听源的场景
目录