实现ref和effect

32 阅读2分钟
// 用于存储依赖关系的 WeakMap,键为目标对象,值为依赖映射
const targetMap = new WeakMap();
// 当前激活的副作用函数,用于依赖收集
let activeEffect = null;
// 创建一个响应式引用对象,用于存储单个值并跟踪依赖
`function ref(initialValue) {
	const refObject = {
		// 存储实际值的私有属性
		_value: initialValue,
		// 当访问 value 属性时,触发依赖收集
		get value() {
			track(refObject, "value"); // 收集依赖
			return this._value;
		},
		// 当设置 value 属性时,触发更新
		set value(newValue) {
			this._value = newValue;
			trigger(refObject, "value"); // 触发更新
		},
	};
	return refObject;
}`

// 注册一个副作用函数,该函数会在依赖的响应式数据变化时自动执行
`function effect(fn) {
	activeEffect = fn; // 设置当前激活的副作用函数
	fn(); // 执行副作用函数以收集依赖
	activeEffect = null; // 重置当前激活的副作用函数
}`

// 依赖收集函数:将当前激活的副作用函数与目标对象的特定属性建立联系
`function track(target, key) {
	if (activeEffect) {
		// 只有在副作用函数执行期间才收集依赖
		let depsMap = targetMap.get(target);
		if (!depsMap) {
			targetMap.set(target, (depsMap = new Map())); // 初始化目标对象的依赖映射
		}
		let dep = depsMap.get(key);
		if (!dep) {
			depsMap.set(key, (dep = new Set())); // 初始化属性的依赖集合
		}
		dep.add(activeEffect); // 将当前副作用函数添加到依赖集合中
	}
}`

// 触发更新函数:当目标对象的特定属性发生变化时,执行所有依赖的副作用函数
`function trigger(target, key) {
	const depsMap = targetMap.get(target);
	if (!depsMap) return; // 没有依赖关系,直接返回
	const dep = depsMap.get(key);
	if (dep) {
		dep.forEach((effect) => effect()); // 执行所有依赖的副作用函数
	}
}`

// 使用示例
`const count = ref(0); // 创建一个初始值为 0 的响应式引用

// 注册一个副作用函数,当 count.value 变化时自动执行
effect(() => {
	console.log("Count is:", count.value); // 首次执行会收集依赖
});

count.value = 1; // 修改值会触发依赖的副作用函数,输出: Count is: 1
count.value = 2; // 再次修改值,输出: Count is: 2
setTimeout(() => {
	count.value = 3; // 延迟修改值,输出: Count is: 3
}, 10000);`