Vue 3 中 watch 的使用详解:监听响应式数据变化的利器

在 Vue 3 的组合式 API(Composition API)中,watch 是一个非常核心且强大的工具,用于监听响应式数据的变化并执行相应的副作用操作。本文将结合实际代码示例,深入讲解 watch 的使用方法、参数配置(如 deepimmediate)、常见写法及其应用场景。


一、watch 基本用法

Vue 3 中的 watch 函数用于监听响应式引用(ref)、响应式对象(reactive)或计算属性(computed)的变化。其基本语法如下:

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

watch(source, callback, options?)
  • source:要监听的数据源,可以是 ref、reactive 对象、getter 函数,或由多个上述组成的数组。
  • callback:当监听的数据发生变化时触发的回调函数,接收新值和旧值作为参数。
  • options (可选):配置选项,如 { deep: true, immediate: true }

二、常见写法与示例分析

1. 监听 ref 或 reactive 对象

复制代码
const count = ref(0)

watch(count, (newVal, oldVal) => {
  console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})

对于 reactive 对象,若只监听整个对象(而非深层属性),默认不会触发深层监听:

复制代码
const user = reactive({ name: 'Alice', age: 25 })

// ❌ 这样不会监听到 user.name 的变化!
watch(user, (newVal, oldVal) => {
  // 不会触发
})

此时需要开启 deep: true(见下文)。


2. 使用 getter 函数监听特定属性(推荐方式)

这是最灵活、最常用的方式,尤其适用于监听 props、route 等非直接 ref 的数据。

示例 1:监听 props 中的复杂对象(如数组)
复制代码
watch(
  () => props.fileList,
  (newFiles, oldFiles) => handleFiles(newFiles, oldFiles),
  { deep: true }
)
  • () => props.fileList:返回一个 getter 函数,确保能正确追踪依赖。
  • deep: true:因为 fileList 很可能是数组或对象,内部元素变化不会触发浅层监听,必须开启深度监听。
  • 回调函数接收新旧值,便于做差异处理(如上传文件列表变更)。

💡 注意:如果 fileList 是一个数组,即使你 push 一个新文件,浅层监听也不会触发,因为数组引用未变。deep: true 能解决这个问题。

示例 2:监听路由路径变化
复制代码
watch(
  () => route.path,
  (newPath) => {
    activeMenu.value = newPath
  }
)
  • route 来自 vue-routeruseRoute(),是一个响应式对象。
  • 监听 route.path 可以在用户切换页面时自动更新高亮菜单项。
  • 此处无需 deep,因为 path 是字符串(原始值),变化即触发。

三、关键配置选项详解

1. deep: true ------ 深度监听

  • 作用:监听对象或数组内部属性的变化。

  • 适用场景:监听嵌套对象、数组、props 中的复杂结构。

  • 注意:性能开销较大,仅在必要时使用。

    const form = reactive({
    user: { name: '', email: '' }
    })

    watch(form, (newVal) => {
    // 默认不触发
    }, { deep: true }) // 开启后,user.name 变化也会触发

⚠️ 对于 ref 包裹的对象,watch(refObj, ..., { deep: true }) 也能深度监听。


2. immediate: true ------ 立即执行

  • 作用:在侦听器创建时立即执行一次回调。

  • 适用场景:初始化时就需要根据当前值执行逻辑(如根据路由加载数据)。

    watch(
    () => route.query.id,
    (id) => {
    if (id) fetchDetail(id)
    },
    { immediate: true }
    )

如果不加 immediate: true,首次进入页面时 id 存在但不会触发 fetchDetail


3. 同时监听多个源

复制代码
watch([refA, refB], ([newA, newB], [oldA, oldB]) => {
  console.log('A or B changed')
})

四、watch vs watchEffect

特性 watch watchEffect
是否需要指定依赖 ✅ 需要显式指定 ❌ 自动追踪
能否访问旧值 ✅ 可以 ❌ 不能
是否立即执行 默认否(可用 immediate 控制) ✅ 总是立即执行
适用场景 需要对比新旧值、精确控制依赖 初始化 + 自动依赖追踪

通常建议优先使用 watch,因为它更明确、可控。


五、最佳实践建议

  1. 监听 props 时,始终使用 getter 写法() => props.xxx

  2. 对对象/数组变化,记得加 deep: true

  3. 需要初始化执行?加 immediate: true

  4. 避免在 watch 中修改被监听的值,可能引发无限循环

  5. 及时清理副作用(如取消请求),可在回调中返回清理函数(类似 useEffect)

    watch(someRef, async (id) => {
    const cancelToken = axios.CancelToken.source()
    try {
    await axios.get(/api/data/${id}, { cancelToken: cancelToken.token })
    } catch (e) {
    if (!axios.isCancel(e)) throw e
    }
    // 返回清理函数
    return () => cancelToken.cancel()
    })


六、总结

watch 是 Vue 3 组合式 API 中处理副作用和响应式数据联动的核心工具。通过合理使用 deepimmediate 等选项,我们可以精准控制监听行为,提升应用的响应性和用户体验。

相关推荐
AlienZHOU2 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc6 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师7 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen8 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒8 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow8 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8188 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800788 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明8 小时前
第 6 章 组件进阶
前端·vue.js
江华森8 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端