Appearance
分支切换的实现原理
本节目标:理解“分支切换的实现原理”的核心思路,并能把它用于实际开发或面试表达。 Effect是一个函数,它首先会执行一次,执行的时候就进行了一次依赖收集; 这次的依赖收集中用到的依赖数据的更新,肯定会触发找到effect,并调用它的run方法; 到此,已经实现了响应式,即数据变化,重新执行run方法; 但是,此时执行run方法时,是一些数据发生了变化,代码可能走了不同的条件判断,执行所依赖的数据可能已经变化了,也就是说有一些数据项我们可能不依赖它了,它的变化,我们不要再更新了! 所以,我们在执行run方法前,把之前可能无效的依赖收集清空,并在此次run的时候,记录下此次依赖的数据项,也就是重新做依赖收集,这样,我们才能在这次用到的依赖项更新时,重新运行run;
原理:effect传入的函数,会首先执行一次,在执行的过程中收集了一次依赖;
接下来发生了依赖值的变化,由于之前依赖收集过,所以,effect的函数自然会重新执行一次,其实这已经实现了响应式;即数据变化了,重新执行了函数;
但由于条件表达式的存在,我们再次运行函数时,有可能我们依赖的值不再是之前的值,也就是说,之前的依赖值我们在这次运行中用不到了,如果这个用不到的依赖值变化时,我们是不需要重新运行函数的,这样可以提高性能;、
由于这次执行函数时,我们可以清楚地知道要依赖哪些值,并且也会进行依赖收集,所以,在执行函数之前,将之前的依赖收集全部清空,在这次运行函数时,我们重新收集,并且,这次收集才是正确的,只有这次计算中的依赖项变化,我们才运行函数,之前的依赖项变化我们不再理会;
接下来,只有上一次运行的函数中用到的依赖项变化了,我传递才找到activeEffect,并重新执行函数;
这一次执行函数,会执行新的代码,确定新的依赖项;同样,我们在运行函数之前,把之前的依赖项清空;也就是将所以属性对应的set中的当前effect删除掉,这们,当属性更新时,我们当前effect是不会重新运行fn的;
<body>
<script src="./reactivity.global.js"></script>
<div id="app"></div>
<script>
const { effect, reactive } = VueReactivity;
const state = reactive({ flag: true, name: 'jw', age: 30 })
effect(() => {
console.log('render')
document.body.innerHTML = state.flag ? state.name : state.age
});
setTimeout(() => {
state.flag = false;
setTimeout(() => {
console.log('修改name,原则上不更新')
state.name = 'zf'
}, 1000);
}, 1000)
</script>
</body>修改src/effect.ts代码
添加一个方法
function cleanupEffect(effect) {
const { deps } = effect;
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect); // 解除effect,重新依赖收集
}
effect.deps.length = 0;
}class ReactiveEffect {
public deps = [];
public parent = null;
public active = true
constructor(public fn) { }
run() {
if (!this.active) {
this.fn();
}
try {
this.parent = activeEffect;
activeEffect = this;
==//== ==这里我们需要在执行用户函数之前将之前收集的内容清空==
==cleanupEffect====(====this====)==
return this.fn();
} finally {
activeEffect = this.parent;
this.parent = null;
}
}
}修改trigger方法
export function trigger(target, type, key, value, oldValue) {
const depsMap = targetMap.get(target);
if (!depsMap) return; // 触发的值不在模板中使用
let effects = depsMap.get(key); // 找到了属性对应的effect
==if== ==(====effects====)== =={==
==effects== ===== ==new== ==Set====(====effects====);==
==effects====.====forEach====(====effect== ===>== =={==
==if== ==(====effect== ==!==== ==activeEffect====)== ==effect====.====run====();==
==})==
==}==
}