在 vue 设计与实现这本书中有一个这样的例子,如下
const data = {
ok: true,
text: "hello world
}
const obj = new Proxy(data, {...})
effect(() => {document.body.innerHtml = obj.ok ? obj.text : "not";})
在这块代码当中,当 obj.ok 为 true,就会将 document.body 的值赋值为 obj.text 的值,也就是说这会触发两个 get,别离是 obj.ok 和 obj.text。
当咱们将 obj.ok 赋值为 false 的时候,会触发这个函数,然而不会进行 obj.text 的 get 操作。
咱们想要的是当 obj.text 发生变化的时候,是不会触发这个函数的,然而当初 text 的 deps 也对这个函数进行依赖收集,这个时候就须要用到 cleanup 函数了
cleanup 函数的实现思维
咱们能够在副作用函数执行的时候,将它从与之相干的依赖汇合中进行删除,当副作用函数执行实现后再进行依赖收集
首先,须要将 effect 函数和 track 函数进行一些革新
function effect(fn) {const effectFn = () =>{cleanup(effectFn)
activeEffect = effectFn
fn()}
effectFn.deps = []
effectFn()}
function track(target, key) {if(!activeEffect) return
let depsMap = bucket.get(target)
if(!depsMap) {bucket.set(target, (depsMap = new Map))
}
let deps = depsMap.get(key)
if(!deps) {depsMap.set(key, (deps = new Set()))
}
deps.add(activeEffect)
// 将与 effecFn 相关联的依赖汇合退出到 effectFn 的 deps 当中
activeEffect.deps.push(deps)
}
在这块代码当中,effectFn 函数执行时会进行 cleanup 操作,再进行 fn 的执行
而 cleanup 函数的实现如下
function cleanup(effectFn) {for(i=0;i<effectFn.deps.length;i++){const deps = effectFn.deps[i]
// 把 effectFn 从与之相关联的 deps 当中删除
deps.delete(effectFn)
}
effectFn.deps.length = 0
}
当咱们将 obj.ok 的值改为 false 时,会进行一次 set 操作,会将 effectFn 从 ok 的 deps 当中取出来,并执行 effectFn,而在 effectFn 当中,会就执行 cleanup 函数,将 effectFn 从相关联的 deps 中删除掉,而后再执行 fn。在执行 fn 时,这个时候就只会触发 obj.ok 的 get 办法,从而将 effectFn 再次退出到 ok 的 deps 当中。