这段代码实现了一个简化版的响应式系统 (类似 Vue 3 的 reactive),包含依赖收集和触发更新的核心机制。让我详细讲解每个部分:
一、核心结构
1. reactive 函数
typescript
function reactive<T extends object>(target: T) {
return createReactiveObject(target);
}
- 入口函数,接收一个普通对象
- 返回该对象的响应式代理
- 使用泛型
T extends object确保只能处理对象类型
2. createReactiveObject 函数
创建 Proxy 代理对象的核心函数:
typescript
function createReactiveObject<T extends object>(target: T) {
const handler = {
// get 拦截器
get(target: object, key: keyof object, receiver: () => void) {
const result = Reflect.get(target, key, receiver);
track(target, key); // 依赖收集
if (typeof result === "object" && result !== null) {
return createReactiveObject(result); // 深度响应式
}
return result;
},
// set 拦截器
set(target: object, key: keyof object, value: unknown, receiver: () => void) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
if (oldValue !== value) {
trigger(target, key); // 触发更新
}
return result;
},
};
return new Proxy(target, handler);
}
关键点:
- 深度响应式:当访问的属性值是对象时,递归创建响应式代理
- 惰性转换:只有在访问嵌套对象时才进行响应式转换
- Reflect API :使用
Reflect.get/set确保正确的this绑定
二、依赖管理系统
1. 存储结构
typescript
const targetMap = new WeakMap<object, Map<string, Set<Function>>>();
let activeEffect: null | Function = null;
结构说明:
text
WeakMap
key: 原始对象 (target)
value: Map
key: 属性名 (string)
value: Set<Function> // 依赖该属性的 effect 集合
为什么用 WeakMap?
- 键是对象,不影响垃圾回收
- 当原始对象不再使用时,对应的依赖关系会自动清除
2. track - 依赖收集
typescript
function track(target: object, key: string) {
if (!activeEffect) return; // 没有 activeEffect 时不收集
// 获取或创建 target 对应的 depsMap
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
// 获取或创建 key 对应的 dep(effect 集合)
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect); // 将当前 effect 加入依赖集合
}
3. trigger - 触发更新
typescript
function trigger(target: object, key: keyof object) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (!dep) return;
// 创建副本避免无限循环
const effects = new Set(dep);
effects.forEach((effect) => {
if (effect !== activeEffect) { // 避免当前 effect 触发自身
effect();
}
});
}
三、Effect 系统
typescr
export function effect(fn: Function) {
const effectFn = () => {
const prevEffect = activeEffect;
activeEffect = effectFn; // 设置当前活跃的 effect
try {
return fn();
} finally {
activeEffect = prevEffect; // 恢复之前的 effect
}
};
effectFn(); // 立即执行一次,进行初始依赖收集
return effectFn;
}
执行流程:
- 创建
effectFn包装函数 - 执行时设置
activeEffect = effectFn - 执行用户传入的
fn() - 在
fn()执行期间,所有对响应式属性的访问都会调用track track将当前activeEffect收集为依赖- 执行完成后恢复之前的
activeEffect
四、使用示例
typescript
const state = reactive<{ todos: string[] }>({ todos: [] });
// 创建一个 effect
effect(() => {
console.log('todos 改变了:', state.todos);
// 首次执行时,访问 state.todos,触发 get
// track 收集当前 effect 作为 todos 的依赖
});
// 修改状态
state.todos.push('学习响应式原理'); // 触发 set -> trigger -> 执行 effect
五、完整工作流程
-
初始化响应式对象
typescriptconst state = reactive({ todos: [] }); // 创建 Proxy 代理 -
创建 effect
typescripteffect(() => console.log(state.todos)); // 1. 设置 activeEffect = 当前 effect // 2. 执行回调,访问 state.todos // 3. Proxy.get 触发,track 收集依赖 -
数据变更
typesstate.todos = ['新任务']; // 1. Proxy.set 触发 // 2. trigger 查找依赖的 effects // 3. 执行所有依赖的 effect 函数