基本定义
- 定义:在Vue 3中,
watch是一个用于响应式地监听数据变化的API。它允许观察一个或多个响应式数据源,并在数据变化时执行回调函数。watch通常用于处理异步操作或复杂逻辑。
基本语法
watch的基本语法如下:
import { watch } from 'vue';
watch(source, callback, options);
- source:需要监听的数据源,可以是一个响应式引用(
ref)、计算属性(computed)或一个返回值的函数。 - callback:数据变化时执行的回调函数,接收新值和旧值作为参数。
- options(可选):配置对象,例如
immediate、deep等。
监听单个数据源
以下是监听单个ref的示例:
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`);
});
监听多个数据源
可以通过数组形式监听多个数据源:
const count = ref(0);
const name = ref('Vue');
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`);
});
监听对象或数组
默认情况下,watch是浅监听。如果需要深度监听对象或数组的变化,可以设置deep: true:
const state = ref({ count: 0 });
watch(
state,
(newValue, oldValue) => {
console.log('state changed', newValue, oldValue);
},
{ deep: true }
);
立即执行回调
设置immediate: true可以让回调在初始化时立即执行:
watch(
count,
(newValue, oldValue) => {
console.log(`count is ${newValue}`);
},
{ immediate: true }
);
监听函数返回的值
可以通过函数返回需要监听的值:
const state = ref({ count: 0 });
watch(
() => state.value.count,
(newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`);
}
);
停止监听
watch返回一个停止函数,调用它可以停止监听:
const stop = watch(count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`);
});
// 停止监听
stop();
注意事项
watch的回调是异步执行的,数据变化后不会立即触发回调。- 如果需要同步响应数据变化,可以使用
watchEffect。 - 避免在回调中修改监听的数据源,否则可能导致无限循环。
代码实现
import { effect, cleanup } from './effect/effect.js'
/**
*
* @param {*} source 响应式数据(对象或者函数)
* @param {*} cb 执行的回调函数
* @param {*} options 选项对象
*/
export function watch(source, cb, options) {
// 参数归一化
let getter = null
if (typeof source === 'function') {
getter = source
} else {
getter = () => traverse(source)
}
let oldValue = null,
newValue = null
const job = () => {
newValue = effectFn()
console.log('newValue', newValue, oldValue)
cb(newValue, oldValue)
oldValue = newValue
}
const effectFn = effect(() => getter(), {
lazy: true,
scheduler: job
})
if(options?.immediate) {
job()
} else {
effectFn()
}
return () => {
cleanup(effectFn)
}
}
function traverse(source, seen = new Set()) {
if (typeof source !== 'object' || source === null || seen.has(source)) return
seen.add(source)
for (const key in source) {
traverse(source[key], seen)
}
return source
}
- 和computed一样,先进行参数归一化,如果是对象,递归遍历对象,保证依赖能够正常收集
- 设置变量存储旧值和新值
- 使用
effect函数的lazy和scheduler,返回effectFn,进行手动控制 - 当依赖的数据变化时,重新运行
effectFn,收集依赖,并且运行回调函数,对oldValue和newValue重新赋值 - 根据传入的
immediate配置,判断是否需要立即运行依赖收集和回调函数

被折叠的 条评论
为什么被折叠?



