watch 与 watchEffect 的区别

Vue 3 中两种侦听器的使用场景和差异

问题

Vue 3 提供了 watchwatchEffect 两种侦听器,它们有什么区别?分别在什么场景下使用?

解答

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 适合需要对比新旧值或精确控制侦听源的场景