Vue.js---分支切换与cleanup
4.2 分支切换与cleanup
1、分支切换
01 const data = { ok: true, text: 'hello world' }
02 const obj = new Proxy(data, { /* ... */ })
03
04 effect(function effectFn() {
05 document.body.innerText = obj.ok ? obj.text : 'not'
06 })
什么是分支切换?就是
document.body.innerText = obj.ok ? obj.text : 'not'
但是这就会导致一个问题“可能会产生遗留的副作用函数”
eg:当obj.ok的值为false的时候,obj.text不会被读取,只会触发obj.ok的读取操作。所以effectFn不应该被obj.text所收集
但是我们目前还是会遗留副作用函数,导致一些不必要的更新
如何解决???
3、清除函数
每次副作用函数执行时,我们可以先把它从所有与之关联的依赖集合中删除
当副作用函数执行完之后会重新建立连接,但在新的连接中是不会包含遗留的副作用函数的
大概思路:在 effect 内部我们定义了新的 effectFn 函数,并为其添加了 effectFn.deps 属性,该属性是一个数组,用来存储所有包含当前副作用函数的依赖集合。
设置deps属性:
function effect (fn) {const effectFn = () => {// 当其执行时,将其设置为当前激活的副作用函数activeEffect = effectFn; fn();}// deps用来存储所有与该副作用函数相关联的依赖集合effectFn.deps = [];// 执行副作用函数effectFn();}
建立依赖集合与effectFn的联系
function track(target , key){if(!activeEffect){return target[key];}// 根据tartget取来的depsMap,它是一个map类型let depsMap = bucket.get(target);// 如果不存在if(!depsMap){// 创建一个bucket.set(target, (depsMap = new Map()));}// 根据key取来的deps,它是一个set类型let deps = depsMap.get(key);// 如果不存在if(!deps){// 创建一个depsMap.set(key, (deps = new Set()));}deps.add(activeEffect); // 添加当前活跃的副作用函数// 存储存在联系的依赖集合,建立起依赖集合和effectFn的关系deps.add(activeEffect);// deps就是一个与当前副作用函数存在联系的依赖集合// 把它存在activeEffect里面的deps中activeEffect.deps.push(deps);}
开始一个个删除联系
function effect (fn) {const effectFn = () => {// 调用clearup函数完成清除工作clearUp(effectFn);// 当其执行时,将其设置为当前激活的副作用函数activeEffect = effectFn; fn();}// deps用来存储所有与该副作用函数相关联的依赖集合effectFn.deps = [];// 执行副作用函数effectFn();}
// 清除函数function clearUp (effectFn){// 遍历然后进行删除for(let i = 0 ; i < effectFn.deps.length ; i++){const deps = effectFn.deps[i];// 移除deps.delete(effectFn);}// 最后重置effectFn.deps数组eff.deps.length = 0;}
我们的清除遗留副作用函数就大功告成啦!但是为什么一直死循环!!?
问题在trigger函数当中
// 触发变化function trigger(target , key , newVal){const depsMap = bucket.get(target);if(!depsMap){return;}const effects = depsMap.get(key);effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数}
在这个函数中,我们遍历effects集合,当副作用函数执行的时候会调用clearUp函数清除,实际上就是从effects集合中将当前执行的副作用函数剔除,但是副作用函数的执行又会导致其重新被收集到集合中,陷入死循环。
如何解决?新构造一个集合并遍历它,代替直接遍历effects函数,就不会出现这样的问题啦
// 触发变化function trigger(target, key, newVal) {const depsMap = bucket.get(target);if (!depsMap) {return;}const effects = depsMap.get(key);const effectsToRun = new Set(effects);effectsToRun.forEach(effectFn => effectFn());// effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数}
造一个集合并遍历它,代替直接遍历effects函数,就不会出现这样的问题啦
// 触发变化function trigger(target, key, newVal) {const depsMap = bucket.get(target);if (!depsMap) {return;}const effects = depsMap.get(key);const effectsToRun = new Set(effects);effectsToRun.forEach(effectFn => effectFn());// effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数}
总结:解决分支问题,主要是clearUp函数在副作用函数执行前清除旧的依赖关系,确保只有当前实际使用的属性变化时才触发更新,值得注意的是我们遍历effects集合,当副作用函数执行的时候会调用clearUp函数清除,实际上就是从effects集合中将当前执行的副作用函数剔除,但是副作用函数的执行又会导致其重新被收集到集合中,陷入死循环,我们要构造一个集合并遍历他