Vue3 watch 如何建立响应式联系

Vue 3 的 watch API 建立响应式联系的核心逻辑位于 packages/runtime-core/src/apiWatch.ts 中。

https://github.com/vuejs/core/blob/main/packages/runtime-core/src/apiWatch.ts#L97

1. watch 的第一个参数是做什么的?

watch 的第一个参数 source 用于“收集依赖”,即追踪你想要监听的数据变化。它可以是响应式数据(如 reactive/ ref)、getter 函数,也可以是多个响应式数据组成的数组。

2. 源码关键片段分析

源码部分如下:

TypeScript

...

export type WatchSource<T = any> = Ref<T, any> | ComputedRef<T> | (() => T) //这里限制watchsource的类型为 ref 计算属性 ()=>T getter方法

...
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
  source: T | WatchSource<T>, // source的类型为传递的soruce本身或者为watchSource所规定的类型
  cb: any,
  options?: WatchOptions<Immediate>,
): WatchHandle {
  ...
  return doWatch(source as any, cb, options)
}

doWatch 里,source 会直接传递给 baseWatch

TypeScript

const watchHandle = baseWatch(source, cb, baseWatchOptions)

baseWatch(在 @vue/reactivity 包里)会根据 source 的类型来决定如何追踪依赖:

  • 如果 source 是响应式数据(ref/ reactive)
    Vue 内部已经对这些数据做了 Proxy 代理,能自动追踪依赖和收集变化。

  • 如果 source 不是响应式数据
    比如直接传一个普通变量或字面量,那么它的变化 Vue 无法感知,这样 watch 就永远不会被触发!

3. 为什么不是响应式数据要用 getter?

getter 本质上是一个函数,Vue 在 watch 时会执行这个函数,并在执行过程中收集依赖。
只有 getter 里读取了响应式数据,Vue 才能追踪它们的变化。

源码体现

比如实例方法 instanceWatch:

TypeScript

const getter = isString(source)
  ? source.includes('.')
    ? createPathGetter(publicThis, source)
    : () => publicThis[source]
  : source.bind(publicThis, publicThis)
...
const res = doWatch(getter, cb.bind(publicThis), options)

这里会把普通的属性名、路径字符串转成一个“getter 函数”,保证 getter 内部访问的是响应式数据。

4. 总结

  • watch 的第一个参数必须是响应式数据,否则变化无法被追踪。

  • 如果不是响应式数据(如普通变量/常量),要用 getter 函数,且 getter 必须访问响应式数据。

  • getter 的存在是为了让 Vue 能够在 getter 执行时收集依赖,实现自动追踪。

举例

js

const count = ref(0)

// 推荐:直接传响应式数据
watch(count, (newVal, oldVal) => { ... })

// 推荐:getter 里读取响应式数据
watch(() => someReactiveObj.value, (newV, oldV) => { ... })

// 错误用法:传普通变量
let foo = 1
watch(foo, ...) // foo 不是响应式,watch 无法监听

结论:
watch 的第一个参数要么是响应式数据,要么是 getter(getter 内部要访问响应式数据),这样 Vue 才能精准跟踪依赖,触发回调。


Vue3 watch 如何建立响应式联系
https://halo.jiangling.site/archives/vue3-watch
作者
你的降灵
发布于
2025年07月23日
许可协议