Vue3 中的响应式是建立在 ES6 的 Proxy 和 Reflect 特性上的。它通过 Proxy 对数据对象的读取和赋值进行拦截,在拦截中添加依赖,在依赖的回调函数中更新视图,从而实现响应式。
effect 虽然开发过程中基本不用,但它是 watch 和 computed 等方法实现的基础。
当我们执行 effect(foo) 时,会执行 foo 函数,在执行的过程中会读取响应式对象上的属性,这时会触发 Proxy 的 get 方法,在 get 方法中会将 effect 函数添加到依赖中。
当响应式对象的属性发生变化时,会触发 Proxy 的 set 方法,在 set 方法中会调用所有依赖的回调函数,也就是 effect 函数,在 effect 函数内部会更新视图,达到响应式的目的。
本文是 Vue3 中响应式原理的简单说明,更详细的实现需要深入源码。
简单分析 effect 函数的定义:
function effect(fn, options) {
const effect = createReactiveEffect(fn, options);
if (!options.lazy) {
effect();
}
return effect;
}
// createReactiveEffect 函数的简化版实现:
function createReactiveEffect(fn, options) {
const effect = function reactiveEffect() {
if (!effect.active) {
effect.active = true;
activeEffect = effect;
fn();
effect.active = false;
activeEffect = null;
}
};
effect.deps = [];
effect.options = options;
return effect;
}
可以看到 effect 函数实际上返回了一个 reactiveEffect 函数,在首次调用 reactiveEffect 时,会执行 fn 函数,也就是我们传入的回调函数,并且在执行 fn 的时候会将 effect 函数设置为 activeEffect,表示它现在是活跃的 effect。
然后当我们读取响应式对象的属性时,会触发对应的 getter,在 getter 中会做如下操作:
// 当然这也是简化版,源码会判断 activceEffect 是否在 Set 中存在
if (activeEffect) {
deps.push(activeEffect);
activeEffect.deps.push(dep);
}
它会将读取属性时的活跃 effect 添加到对应的 dep (依赖) 中,并且将 dep 添加到 effect 的 deps 数组中,表示 effect 依赖于哪些属性的变化。
当响应式对象的属性发生变化时,它会遍历所有的依赖,即 deps中存放的 effect 函数,并执行,此时 effect 就会重新执行 fn 函数,达到更新视图的目的。
通过上面的分析,我们可以看到 Vue3 中响应式的实现原理,就是通过 Proxy 拦截对象属性的读取和赋值,维护每个属性的依赖,当属性变化时重新执行依赖,从而更新视图。