Vue 监听器 watch 深度解析

前言

小编最近在复习vue,复习到vue的监听器,总结了以下几点:

完整代码示例

javascript 复制代码
watch(
  user, // 监听源
  (newVal) => { // 回调函数
    console.log('用户信息变化:', newVal)
  },
  { deep: true } // 配置选项
)

一、核心概念拆解

1. 三大核心要素

要素 说明 类比生活场景
监听源 被监视的数据源(用户信息对象) 监控摄像头对准的保险柜
回调函数 数据变化时触发的操作函数 保险柜被打开时触发的警报
配置选项 控制监听行为的参数设置 监控摄像头的灵敏度设置

2. 参数详解

javascript 复制代码
watch(source, callback, options)

二、逐层深入解析

1. 监听源(Source)

常见类型

  • 响应式对象reactive 创建)

  • ref 对象(含对象类型的 ref)

  • getter 函数() => obj.prop

js 复制代码
//来自官方文档
const x = ref(0)
const y = ref(0)

// single ref
watch(x, (newX) => {
  console.log(`x is ${newX}`)
})

// getter
watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  }
)

// array of multiple sources
watch([x, () => y.value], ([newX, newY]) => {
  console.log(`x is ${newX} and y is ${newY}`)
})

可能这里有人对getter函数不太理解,下面是我找的资料:

一、本质理解

getter(获取器)这个名字来源于它的核心功能:获取并返回一个值。就像去自动售货机买东西:

  1. 普通函数:你按按钮 → 机器给你饮料(直接获取)
  2. getter函数:你按特定组合键 → 机器计算总价后给你结果(需要计算过程)

二、代码对照表

代码部分 现实类比 为什么叫 getter
() => x.value + y.value 收银员计算商品总价 需要"获取"计算结果
watch(..., (sum) => {}) 收银员发现总价变化时通知你 监控获取器返回值的变动

三、深入解析

1. 基本特征

  • 输入:依赖其他值(x/y)
  • 输出:计算后的新值(sum)
  • 特性:每次访问都会重新计算(除非缓存)

2. 与普通函数的区别

javascript 复制代码
// 普通函数
function getSum() {
  return x + y // 直接返回
}

// getter函数(在watch中)
() => x.value + y.value // 被监控的依赖关系,记住返回的是一个函数。若
const x = ref(1);
const y = ref(2);
const xy = () => x.value + y.value;
console.log(xy);//() => x.value + y.value
console.log(xy());// 3
需要调用才有值,或者使用computed属性

3. Vue 的特别处理

当把这个函数传给 watch 时:

  1. 依赖追踪:Vue 自动记录函数内部访问的响应式变量(x.value/y.value)
  2. 重新计算:当任意依赖变化时,自动重新执行这个函数
  3. 触发回调:如果返回的新值 ≠ 旧值,就执行回调函数

四、为什么必须用这种写法?

1. 错误写法示例

js 复制代码
// 错误!无法追踪依赖
watch(x.value + y.value, (sum) => {})

2. 正确写法原理

通过函数包装:

js 复制代码
// ✅ 正确!建立响应式依赖链
watch(
  () => x.value + y.value, // 依赖收集器
  (sum) => { /* 响应变化 */ }
)

五、类比其他场景

1. 计算属性中的 getter

js 复制代码
const total = computed({
  get: () => x.value + y.value, // 同款getter
  set: (val) => { /*...*/ }
})

2. 对象属性的 getter

js 复制代码
const obj = {
  get sum() { // 原生JS的getter
    return x + y
  }
}

特殊特性

  • 当监听整个对象时,默认不触发嵌套属性的变化
  • 需要配合 deep: true 才能深度监听

2. 回调函数(Callback)

参数解析

javascript 复制代码
(newValue, oldValue) => { /*...*/ }
  • 第一个参数:变化后的新值
  • 第二个参数:变化前的旧值
  • 注意 :对于对象类型,oldValue 会与 newValue 指向同一引用

3. 配置选项(Options)

选项 类型 默认值 作用说明
deep boolean false 深度监听嵌套属性变化
immediate boolean false 立即触发回调(初始值时)
flush string 'pre' 控制回调触发时机(pre/post/sync)

三、深度监听原理

1. 工作机制图解

graph TD A[user 对象] --> B[属性变更] B --> C{deep: true?} C -->|是| D[遍历所有子属性] C -->|否| E[仅监听顶层属性] D --> F[触发回调] E --> G[不触发回调]

2. 典型使用场景

  • 用户资料表单(嵌套多个字段)
  • 购物车商品对象(多层数据结构)
  • 复杂配置项对象

四、对比其他监听方式

1. watch vs watchEffect

特性 watch watchEffect
依赖收集方式 显式指定监听源 自动收集回调中的依赖
初始执行 需配置 immediate: true 立即执行
适用场景 精确控制监听目标 副作用操作(如日志记录)

2. 与 Vue 2 的对比

javascript 复制代码
// Vue 2 选项式 API
watch: {
  user: {
    handler(newVal) { /*...*/ },
    deep: true
  }
}

// Vue 3 组合式 API(更灵活)
const user = reactive({/*...*/})
watch(user, (newVal) => {/*...*/}, { deep: true })

五、最佳实践指南

1. 性能优化建议

  • 避免过度深度监听 :只对必要对象开启 deep

  • 使用精确监听路径

    javascript 复制代码
    // 优于深度监听
    watch(() => user.address.city, (newCity) => {...})
  • 及时清理监听

    javascript 复制代码
    const stopWatch = watch(...)
    onUnmounted(stopWatch) // 组件卸载时停止监听

2. 常见错误示例

javascript 复制代码
// 错误1:直接修改监听源导致无限循环
watch(user, (newVal) => {
  user.name = '新名字' // 会再次触发监听!
})

// 错误2:忘记处理异步操作
watch(user, async (newVal) => {
  const data = await fetchData() // 需要处理可能的竞态条件
})

// 正确解决方案:使用 watchEffect + 清理函数
watchEffect(async (onCleanup) => {
  let isValid = true
  onCleanup(() => { isValid = false })
  
  const data = await fetchData()
  if (isValid) { /* 处理数据 */ }
})

六、完整示例场景

用户资料编辑监控

javascript 复制代码
const user = reactive({
  id: 1,
  info: {
    name: '张三',
    address: {
      city: '北京',
      street: '朝阳区'
    }
  }
})

// 深度监听整个用户对象
watch(
  user,
  (newUser) => {
    console.log('用户信息已修改,自动保存...')
    autoSave(newUser)
  },
  { deep: true, immediate: true }
)

// 精确监听城市变化
watch(
  () => user.info.address.city,
  (newCity) => {
    updateMap(newCity)
  }
)

关键总结

  1. 使用 deep: true 时要考虑性能影响
  2. 优先使用精确监听路径替代深度监听
  3. 注意对象引用的特性,必要时使用深拷贝
  4. 异步操作要配合清理函数防止内存泄漏

官方文档参考:Vue 3 watch

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax