书接上回,前面我们介绍了watch和computed,这篇文章说一下watchEffect。watchEffect和watch非常像,但是watchEffect不用指定要追踪的响应式数据,也不需要指定响应式数据变更时回调,先来看一下文档怎么说:
可以发现这就是最普通的响应式系统的用法:立即执行一个函数,执行函数的时候响应式依赖会收集这个函数,然后函数的依赖变化后,会触发函数的重新执行
还记得我们之前说watch的时候,watch的第一个参数可以接受一个getter。所以其实watchEffet就是第一个参数为含有响应式变量的getter,第二个参数为null的watch。 所以我们可以给出一个实现:
// 没有第二个参数的watch,也不需要往fn中传入oldValue和newValue
function watchEffect(fn) {
let getter = fn
effect(() => {
getter()
})
}
不过实际上源码中WatchEffect因为是一种特殊的watch,watch要兼容一下watchEffect的设计:
function watchEffect(fn) {
watch(fn, null)
}
function watch(source, callback) {
let getter
if (typeof source === 'function') {
// 如果source是getter
// source为getter还可能是watchEffect
getter = source
} else if (Array.isArray(source)) {
// 如果是数组
getter = () =>
source.map((val) => {
// 我只处理了数组中只有普通对象和getter
if (typeof source === 'function') {
return val()
} else {
traverse(val)
}
})
} else {
getter = () => {
traverse(source)
}
}
let value
let prevValue
// 前面讲到过,如果lazy为true,
// 就返回副作用函数的包装函数,这个包装函数返回副作用函数的值
const effectFn = effect(
() => {
// 执行封装好的getter,读取数据
getter()
},
{
lazy: true,
scheduler() {
// 说明是watch
if (callback) {
// 调度函数内执行callback
value = effectFn()
callback(value, prevValue)
prevValue = value
} else {
// watch为了兼容watchEffect,在调度函数内嵌套执行effectFn()
effectFn()
}
}
}
)
if (callback) {
// 因为懒执行,所以手动调用进行读取操作建立响应联系
// 数据变更调用scheduler的时候,会重新拿到新值
prevValue = effectFn()
} else {
// watchEffect首次运行并收集依赖
effectFn()
}
}
在上面的代码中,我们根据有没有回调,来判断是watch和watchEffect,就实现了watchEffect。