返回
创建于
状态公开

我们来一步步实现一个简化版 Vue3 响应式系统,用到 WeakMap + Proxy —— 这就是 WeakMap 在框架底层的经典、最强应用场景之一。


🧩 一、设计目标

我们要实现的功能:

✅ 1. 任意对象变为“响应式”(reactive) ✅ 2. 访问属性时自动“收集依赖” ✅ 3. 修改属性时自动“触发更新” ✅ 4. 使用 WeakMap 存储对象 → 属性 → 依赖关系 ✅ 5. 自动避免内存泄漏


🧠 二、基本结构示意

plaintext
1targetMap (WeakMap)
2  └── target (对象)
3        └── depsMap (Map)
4              └── key → Set(effect)

解释:

  • targetMap:WeakMap,用于保存每个“响应式对象”的依赖;
  • depsMap:Map,每个 key 对应一组副作用函数(Set);
  • 当读取属性时 → 记录依赖;
  • 当修改属性时 → 触发依赖。

⚙️ 三、完整实现代码(可直接运行)

js
1// 🌱 存储所有对象依赖关系
2const targetMap = new WeakMap();
3
4// 当前正在执行的副作用函数
5let activeEffect = null;
6
7// 收集依赖
8function track(target, key) {
9  if (!activeEffect) return;
10
11  let depsMap = targetMap.get(target);
12  if (!depsMap) {
13    depsMap = new Map();
14    targetMap.set(target, depsMap);
15  }
16
17  let dep = depsMap.get(key);
18  if (!dep) {
19    dep = new Set();
20    depsMap.set(key, dep);
21  }
22
23  dep.add(activeEffect);
24}
25
26// 触发依赖更新
27function trigger(target, key) {
28  const depsMap = targetMap.get(target);
29  if (!depsMap) return;
30  const dep = depsMap.get(key);
31  if (dep) {
32    dep.forEach(effect => effect());
33  }
34}
35
36// 创建响应式对象
37function reactive(target) {
38  return new Proxy(target, {
39    get(obj, key, receiver) {
40      track(obj, key);
41      return Reflect.get(obj, key, receiver);
42    },
43    set(obj, key, value, receiver) {
44      const result = Reflect.set(obj, key, value, receiver);
45      trigger(obj, key);
46      return result;
47    }
48  });
49}
50
51// 定义副作用函数
52function effect(fn) {
53  const wrapper = () => {
54    activeEffect = wrapper;
55    fn();
56    activeEffect = null;
57  };
58  wrapper();
59}
60
61// 🌟 使用示例
62const state = reactive({ count: 0, msg: 'hello' });
63
64effect(() => {
65  console.log('count 改变了 →', state.count);
66});
67
68state.count++; // 输出:count 改变了 → 1
69state.count++; // 输出:count 改变了 → 2

🧩 四、解释关键点

模块作用
targetMap使用 WeakMap 存放所有响应式对象
Map(内部)记录每个属性的依赖集合
Set(内部)依赖函数集合(无重复)
track()在读取属性时注册依赖
trigger()在修改属性时触发依赖
WeakMap 的好处当对象被释放时,对应依赖自动清除,不会内存泄漏

🌳 五、WeakMap 的意义

这一点非常关键:

  • targetMap 是一个 WeakMap

  • state(被代理对象)不再被引用时:

    • 它会被垃圾回收;
    • targetMap 中的记录自动消失;
    • 无需手动清理依赖;
    • 不会引起内存泄漏。

🔥 这正是 Vue3、MobX、SolidJS 等框架使用 WeakMap 的核心原因。


🧱 六、扩展:嵌套对象支持(递归代理)

如果我们要让内部对象也响应式,只要在 get 时递归代理:

js
1function reactive(target) {
2  return new Proxy(target, {
3    get(obj, key, receiver) {
4      const result = Reflect.get(obj, key, receiver);
5      track(obj, key);
6      if (typeof result === 'object' && result !== null) {
7        return reactive(result); // 递归代理
8      }
9      return result;
10    },
11    set(obj, key, value, receiver) {
12      const result = Reflect.set(obj, key, value, receiver);
13      trigger(obj, key);
14      return result;
15    }
16  });
17}

🧩 七、最终效果演示

js
1const appState = reactive({
2  user: { name: 'Alice', age: 18 },
3  count: 0
4});
5
6effect(() => {
7  console.log(`用户:${appState.user.name}, 年龄:${appState.user.age}`);
8});
9
10appState.user.name = 'Bob';  // 自动触发 effect

输出:

js
1用户:Alice, 年龄:18
2用户:Bob, 年龄:18

🧠 八、总结

技术作用
WeakMap保存对象 → 属性依赖关系,自动清理
Proxy拦截 get/set
effect()注册依赖
track / trigger实现依赖收集与触发
优点自动化依赖追踪 + 无内存泄漏