响应式系统的依赖收集核心逻辑,主要用于管理“目标对象”(target
)与“属性”(key
)的依赖关系(Dep
)。以下是 targetMap
、depsMap
和 dep
的关系,以及它们的作用和实际应用场景。
1. 关系分析
目标:
- 将
target
(目标对象)与key
(对象的某个属性)之间的依赖关系映射起来,用于响应式更新。
三者的关系:
targetMap
:- 全局的依赖映射表。
- Key: 响应式对象(
target
)。 - Value:
depsMap
,存储该target
的属性依赖。
depsMap
:- 针对某个具体
target
的属性依赖映射表。 - Key: 属性名称(
key
)。 - Value:
dep
,存储当前属性的依赖。
- 针对某个具体
dep
:- 依赖集合。
- 实际上是一个
Dep
实例,负责存储订阅当前属性的所有副作用(watcher
或effect
)。
结构图:
targetMap
└── target1 (e.g., obj1)
└── depsMap
├── key1 (e.g., "name")
│ └── dep (Dep 实例,存储依赖)
├── key2 (e.g., "age")
│ └── dep
└── target2 (e.g., obj2)
└── depsMap
├── key1
│ └── dep
├── key2
└── dep
2. 代码解读
核心逻辑:
检查全局依赖映射表是否有对应的
target
(目标对象)。- 如果没有,创建一个新的
depsMap
并存储到targetMap
。
- 如果没有,创建一个新的
检查
depsMap
中是否有对应的key
(属性)。- 如果没有,为该属性创建一个新的
Dep
实例,并存储到depsMap
。
- 如果没有,为该属性创建一个新的
返回
dep
实例,供依赖收集系统使用。
代码详解:
function getDep(target, key) {
// 从全局依赖映射表中获取目标对象的依赖映射
let depsMap = targetMap.get(target);
// 如果目标对象没有被记录到 targetMap 中,初始化 depsMap
if (!depsMap) {
depsMap = new Map(); // 用于存储 target 的属性依赖
targetMap.set(target, depsMap);
}
// 从 depsMap 中获取当前属性的依赖
let dep = depsMap.get(key);
// 如果当前属性还没有依赖,初始化 Dep 实例
if (!dep) {
dep = new Dep(); // 用于存储该属性的依赖
depsMap.set(key, dep);
}
// 返回当前属性的依赖实例
return dep;
}
3. 实际应用场景
响应式系统(类似 Vue 的实现):
以下是基于这段代码的响应式实现,简化版示例:
activeEffect当前副作用 effect
const targetMap = new WeakMap(); // 全局依赖映射表
// 定义依赖收集类
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect); // 将副作用函数添加到依赖中
}
}
notify() {
this.subscribers.forEach(effect => effect()); // 触发所有依赖
}
}
// 当前活动的副作用函数
let activeEffect = null;
function effect(fn) {
activeEffect = fn;
fn(); // 立即执行副作用函数,触发依赖收集
activeEffect = null;
}
// 依赖收集工具函数
function track(target, key) {
const dep = getDep(target, key);
dep.depend();
}
// 触发更新工具函数
function trigger(target, key) {
const dep = getDep(target, key);
dep.notify();
}
// 创建响应式对象
function reactive(target) {
return new Proxy(target, {
get(obj, key) {
track(obj, key); // 收集依赖
return Reflect.get(obj, key);
},
set(obj, key, value) {
const result = Reflect.set(obj, key, value);
trigger(obj, key); // 触发依赖更新
return result;
}
});
}
// 示例使用
const state = reactive({ count: 0 });
effect(() => {
console.log(`Count has changed to: ${state.count}`);
});
state.count++; // Console: Count has changed to: 1
state.count = 5; // Console: Count has changed to: 5
4. 依赖管理的意义
- 高效更新:
- 每个属性都有独立的依赖集合,只通知真正需要更新的部分,提升性能。
- 解耦逻辑:
- 通过依赖收集和通知机制,视图更新和业务逻辑实现了解耦。
总结
targetMap
: 全局存储所有响应式对象及其属性依赖的映射。depsMap
: 针对某个对象存储属性与依赖的映射。dep
: 某个属性的依赖集合,负责管理订阅的副作用。