watch
的作用是监听数据的变化,当数据发生变化时,执行一个回调函数,它的实现依赖于 Vue 的响应式系统(reactive
和ref
)
案例
通过以下案例来理解,首先引入reactive
、effect
、watch
三个函数,声明obj
响应式数据,接着执行watch
函数,第一个参数为监听数据,第二个参数为监听回调,最后两秒后修改obj
的name
值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="../../dist/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const { reactive, effect, watch } = Vue
// 1. reactive 构建响应性数据
const obj = reactive({
name: 'jc'
})
// 2. 执行了 watch 函数
watch(obj, (value, oldValue) => {
console.log('watch 触发了')
console.log(value)
})
// 3. 两秒后触发 setter 行为
setTimeout(() => {
obj.name = 'cc'
}, 2000)
</script>
</body>
</html>
doWatch
方法
watch
函数定义在packages/runtime-core/src/apiWatch.ts
文件下:
该函数实际执行的是doWatch
方法,它会根据传入的参数( source
和 cb
)生成一个监听器
1)根据 source
的类型生成 getter
getter
是一个函数,用于获取监听数据的值,根据source
的类型:
reactive
类型,直接返回source
ref
类型,返回source.value
- 如果
source
是一个函数,getter
会执行这个函数
2)定义 job
函数
job
是watch
的核心逻辑,它会在数据变化时执行,它的主要任务是:
- 获取新的值
newValue
- 检查新值和旧值是否不同
- 如果不同,执行回调函数
cb
3)调度器 scheduler
scheduler
决定了job
的执行时机,Vue 提供了三种调度方式:
sync
:同步执行post
:在渲染后执行pre
:在渲染前执行(默认)
function doWatch(
source: WatchSource | WatchSource[] | WatchEffect | object,
cb: WatchCallback | null,
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
// 省略
const instance = currentInstance
let getter: () => any
let forceTrigger = false
let isMultiSource = false
if (isRef(source)) {
// 是否 ref 类型
getter = () => source.value
forceTrigger = isShallow(source)
} else if (isReactive(source)) {
// 是否 reactive 类型
getter = () => source
deep = true // 主动开启 深度监听
} else if (isArray(source)) {
isMultiSource = true
forceTrigger = source.some(s => isRe