Vue2 和 Vue3 中 watch 用法和原理详解

@TOC

1. Vue2 中的 watch

1. 基本用法

在 Vue2 中,watch 是一个对象,其键是要观察的表达式,值是对应的回调函数或包含选项的对象。

javascript 复制代码
// 对象写法
export default {
  data() {
    return {
      count: 0,
      user: {
        name: 'John',
        age: 25
      }
    }
  },
  watch: {
    // 监听基本数据类型
    count(newVal, oldVal) {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    },
    
    // 深度监听对象
    user: {
      handler(newVal, oldVal) {
        console.log('user changed:', newVal)
      },
      deep: true, // 深度监听
      immediate: true // 立即执行
    },
    
    // 监听对象特定属性
    'user.name': function(newVal, oldVal) {
      console.log(`name changed from ${oldVal} to ${newVal}`)
    }
  }
}

2. 程序式监听

Vue2 也提供了 $watch API,可以在实例的任何地方监听数据变化。

javascript 复制代码
export default {
  mounted() {
    // 使用 $watch API
    const unwatch = this.$watch(
      'count',
      (newVal, oldVal) => {
        console.log(`count changed: ${oldVal} -> ${newVal}`)
      },
      {
        immediate: true,
        deep: false
      }
    )
    
    // 取消监听
    // unwatch()
  }
}

2. Vue3 中的 watch

1. 组合式 API 用法

Vue3 的 watch 更加灵活,支持监听 ref、reactive 对象、getter 函数等多种数据源。

javascript 复制代码
import { ref, reactive, watch, watchEffect } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const user = reactive({
      name: 'John',
      age: 25
    })
    
    // 监听 ref
    watch(count, (newVal, oldVal) => {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    })
    
    // 监听 reactive 对象
    watch(
      () => user.name, // getter 函数
      (newVal, oldVal) => {
        console.log(`name changed from ${oldVal} to ${newVal}`)
      }
    )
    
    // 深度监听对象
    watch(
      () => user,
      (newVal, oldVal) => {
        console.log('user changed:', newVal)
      },
      { deep: true }
    )
    
    // 监听多个源
    watch(
      [() => count.value, () => user.name],
      ([newCount, newName], [oldCount, oldName]) => {
        console.log(`count: ${oldCount}->${newCount}, name: ${oldName}->${newName}`)
      }
    )
    
    // watchEffect - 自动追踪依赖
    watchEffect(() => {
      console.log(`count is ${count.value}, name is ${user.name}`)
    })
    
    return {
      count,
      user
    }
  }
}

2. 选项式 API 用法

Vue3 也支持在选项式 API 中使用 watch,与 Vue2 的用法类似。

javascript 复制代码
import { watch } from 'vue'

export default {
  data() {
    return {
      count: 0,
      user: {
        name: 'John',
        age: 25
      }
    }
  },
  watch: {
    count(newVal, oldVal) {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    }
  },
  created() {
    // 使用 watch 函数
    watch(
      () => this.user.name,
      (newVal, oldVal) => {
        console.log(`name changed from ${oldVal} to ${newVal}`)
      }
    )
  }
}

3.核心原理分析

1. Vue2 的 Watch 原理

Vue2 的 watch 基于响应式系统的依赖收集和派发更新机制。

  • 在组件实例初始化阶段,遍历 watch 对象的每一个属性,为每一个监听表达式创建一个 watcher 实例。
  • watcher 的创建过程:解析表达式,生成 getter 函数;执行 getter 函数,触发依赖收集;保存旧值,等待数据变化。
  • 当被监听的数据发生变化时,触发 setter,通知对应的 watcher 更新;watcher 执行 getter 获取新值,比较新值和旧值,如果不同则执行回调函数。

2. Vue3 的 Watch 原理

Vue3 的 watch 基于 effect 机制实现。

  • 将回调函数包装成一个 effect,当被监听的数据发生变化时,effect 会重新执行。
  • 通过 track 函数进行依赖收集,trigger 函数触发更新。
  • 使用调度器 scheduler 控制 effect 的执行时机,实现异步更新和 flush 选项。

4. 主要差异对比

1. 差异总结

  • Vue2 的 watch 语法较为简单直观,适合选项式 API;Vue3 的 watch 更加灵活,适合组合式 API。
  • Vue3 的 watch 基于 effect 机制实现,提供了更好的性能和更丰富的配置选项。
  • 两者都支持深度监听、立即执行、异步回调等特性,但在语法和使用方式上有所不同。

2. 特性对比

特性 Vue2 Vue3
API 形式 选项式 组合式 + 选项式
监听 reactive 不支持 原生支持
深度监听 需要显式配置 reactive 对象默认深度监听
多源监听 不支持 支持监听多个数据源
清理副作用 不支持 支持 cleanup 函数
性能 相对较低 基于 Proxy,性能更好

5. 使用建议

1. 性能优化

避免不必要的深度监听,只监听需要的属性。

javascript 复制代码
// Vue3 - 避免不必要的深度监听
const largeObject = reactive({ /* 大量数据 */ })

// 不好的做法
watch(largeObject, () => {
  // 任何属性变化都会触发
})

// 好的做法 - 只监听需要的属性
watch(
  () => largeObject.importantProp,
  () => {
    // 只有 importantProp 变化时触发
  }
)

2. 清理副作用

Vue3 支持在 watch 中清理副作用,避免内存泄漏。

javascript 复制代码
// Vue3 - 清理副作用
watch(
  data,
  async (newVal, oldVal, onCleanup) => {
    let cancelled = false
    onCleanup(() => {
      cancelled = true
    })
    
    const result = await fetchData(newVal)
    if (!cancelled) {
      // 处理结果
    }
  }
)

3. 防抖处理

使用防抖函数避免频繁触发 watch 回调。

javascript 复制代码
import { debounce } from 'lodash-es'

// Vue3 防抖监听
watch(
  searchQuery,
  debounce((newVal) => {
    searchAPI(newVal)
  }, 300)
)

6.常见问题解答

1. Vue2 和 Vue3 的 watch 混用?

在 Vue3 的选项式 API 中,可以继续使用 Vue2 风格的 watch 选项,但不建议混用。

2. 什么时候用 watch,什么时候用 computed?

watch 用于执行副作用(如 API 调用、DOM 操作),computed 用于派生数据。

3. watchEffect 和 watch 的区别?

watchEffect 自动追踪依赖,立即执行;watch 需要明确指定监听源,默认懒执行。

通过深入理解 Vue2 和 Vue3 中 watch 的用法和原理,可以更好地根据项目需求选择合适的监听方式,并编写出更高效、可维护的代码。

相关推荐
恋猫de小郭5 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅12 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606112 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了12 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅12 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅13 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment13 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅14 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊14 小时前
jwt介绍
前端