欢迎光临仙桃市分类吧
详情描述

watchEffect 是 Vue3 响应式系统的核心 API 之一,它自动追踪依赖并在依赖变化时重新执行回调函数。下面从核心用法和原理两个维度进行解析:

一、核心用法

1. 基本使用

import { watchEffect, ref } from 'vue'

const count = ref(0)

// 自动追踪依赖,当 count 变化时重新执行
watchEffect(() => {
  console.log(`count is: ${count.value}`)
})

2. 清理副作用

watchEffect((onCleanup) => {
  const timer = setInterval(() => {
    console.log('tick')
  }, 1000)

  // 清理函数:在下一次执行前或停止监听时调用
  onCleanup(() => clearInterval(timer))
})

3. 配置选项

watchEffect(
  () => { /* ... */ },
  {
    flush: 'post',     // 'pre' | 'post' | 'sync'
    onTrack(e) {       // 调试:追踪依赖时触发
      debugger
    },
    onTrigger(e) {     // 调试:依赖变化触发重新执行时
      debugger
    }
  }
)

4. 停止监听

const stop = watchEffect(() => { /* ... */ })

// 手动停止
stop()

二、原理解析

1. 依赖自动追踪机制

// 简化版原理示意
function watchEffect(effect) {
  let cleanup

  const reactiveEffect = new ReactiveEffect(() => {
    // 执行清理函数
    if (cleanup) cleanup()

    // 开启依赖收集
    effectTrackDepth++
    activeEffect = reactiveEffect

    // 执行用户回调,自动收集依赖
    const result = effect(onCleanup => {
      cleanup = onCleanup
    })

    // 结束收集
    effectTrackDepth--
    activeEffect = undefined

    return result
  })

  reactiveEffect.run()
  return () => reactiveEffect.stop()
}

2. 响应式系统交互

执行流程:
1. 创建 ReactiveEffect 实例
2. 执行 effect.run() 
3. → 触发 track() 开始依赖收集
4. → 执行用户回调,访问响应式数据
5. → 响应式数据的 getter 触发,将当前 effect 添加到依赖集合
6. → 回调执行完毕,结束收集
7. → 响应式数据变化时触发 trigger()
8. → 找到对应的 effect 并重新执行

3. flush 时机控制

  • pre(默认):组件更新前执行
  • post:组件更新后执行,可访问到 DOM
  • sync:同步执行,响应式数据变化后立即执行

三、与 watch 的区别

特性 watchEffect watch
依赖收集 自动 显式指定
初始执行 立即执行 可配置
访问新旧值 不支持 支持
使用场景 副作用聚合 特定响应式源变化

四、最佳实践

1. 适用场景

// 场景1:自动依赖多个数据
watchEffect(() => {
  fetchData(query.value, filter.value)
})

// 场景2:DOM 操作(使用 flush: 'post')
watchEffect(() => {
  if (isOpen.value) {
    nextTick(() => inputRef.value?.focus())
  }
}, { flush: 'post' })

2. 注意事项

// ❌ 避免异步回调中的依赖追踪
watchEffect(async () => {
  // await 之后的响应式访问不会被追踪!
  const data = await fetch(url)
  console.log(someRef.value) // 不会被追踪
})

// ✅ 正确的异步用法
watchEffect(() => {
  const id = userId.value // 同步收集依赖
  fetchUser(id).then(user => {
    // 这里可以访问,但不会建立响应式关联
  })
})

3. 性能优化

// 使用响应式对象而非多个 ref
const filters = reactive({
  keyword: '',
  status: 'active',
  page: 1
})

// 单个 watchEffect 替代多个 watch
watchEffect(() => {
  search(filters.keyword, filters.status, filters.page)
})

五、源码关键设计

EffectScope:管理 effect 的生命周期,组件卸载时自动清理 ReactiveEffect:封装副作用函数,提供调度和停止能力 全局依赖栈:处理嵌套 effect 的依赖收集 调度器(scheduler):控制 effect 的执行时机和顺序

watchEffect 体现了 Vue3 响应式系统的设计哲学:自动追踪 + 显式控制,既提供了便利性,又通过 API 设计避免了过度“魔法”。