Vue3 的 effect 简单说明

77 阅读2分钟

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 拦截对象属性的读取和赋值,维护每个属性的依赖,当属性变化时重新执行依赖,从而更新视图。