tangyuxian
文章93
标签43
分类6

文章分类

文章归档

vue-vue3的响应式原理的理解

vue-vue3的响应式原理的理解

Vue 3 的依赖收集核心是基于 Proxy。读取数据时(触发 get),通过 track 函数把当前的副作用函数(如组件渲染函数)收集起来;在修改数据时(触发 set),通过 trigger 函数找到并执行这些副作用函数。

1. 核心总结

“Vue 3 的响应式系统是基于 Proxy 实现的。它包含两个核心过程:

  1. 依赖收集 (track):发生在读取数据(get)时,将当前的副作用函数(如组件渲染函数)存储起来。
  2. 派发更新 (trigger):发生在修改数据(set)时,取出之前收集的副作用函数并执行,从而更新视图。”

2. 核心数据结构 (TargetMap)

Vue 3 使用了一个全局的 WeakMap (targetMap)存依赖:

  • WeakMap 的 Key 是原始对象 (Target)
  • Value 是一个 Map (depsMap),它的 Key 是属性名
  • depsMap 的 Value 是一个 Set (dep),里面存放着所有相关的副作用函数 (Effect)

简单说就是:Target -> Key -> Effect集合。”

3. 详细流程解析

阶段一:依赖收集 (Track) - “读的时候记账”

  • 当我们在组件渲染或计算属性中读取 state.count 时,触发 Proxy 的 get 拦截。
  • get 内部调用 track(target, key)
  • track 函数会检查全局变量 activeEffect(当前正在运行的副作用)。
  • 如果有,就去 targetMap 中找到对应的 Set,把 activeEffect 加进去。

阶段二:派发更新 (Trigger) - “写的时候通知”

  • 当我们执行 state.count = 2 时,触发 Proxy 的 set 拦截。
  • set 内部首先通过 Reflect.set 更新数据值。
  • 然后调用 trigger(target, key)
  • trigger 函数通过 targetkeytargetMap 中查找,找到对应的 Set(依赖集合)。
  • 遍历这个 Set,执行里面所有的副作用函数,视图因此更新。

📊 完整原理图解

image-20260122105324316


💻 简版源码

这段代码展示了从 reactivetrack 再到 trigger 的完整逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// --- 1. 全局变量 & 副作用函数 ---
let activeEffect = null;

function effect(fn) {
  activeEffect = fn;
  fn(); // 执行 fn,触发 get -> 触发 track
  activeEffect = null;
}

// --- 2. 依赖存储结构 ---
// WeakMap { Target -> Map { Key -> Set [Effect] } }
const targetMap = new WeakMap();

// --- 3. 依赖收集 (Track) ---
function track(target, key) {
  if (!activeEffect) return;

  // 1. 找 target 对应的 depsMap
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }

  // 2. 找 key 对应的 dep (Set)
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }

  // 3. 收集当前副作用
  dep.add(activeEffect);
}

// --- 4. 派发更新 (Trigger) ---
function trigger(target, key) {
  // 1. 也就是根据 target 和 key 找到 dep
  const depsMap = targetMap.get(target);
  if (!depsMap) return;

  const dep = depsMap.get(key);
  
  // 2. 执行所有依赖 (构造新的 Set 防止循环引用问题)
  if (dep) {
    const effectsToRun = new Set(dep);
    effectsToRun.forEach(effectFn => effectFn());
  }
}

// --- 5. 响应式核心 (Reactive) ---
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      // 关键:收集依赖
      track(target, key);
      return result;
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      // 关键:触发更新 (注意:要在 set 成功之后)
      trigger(target, key);
      return result;
    }
  });
}

// --- 测试 ---
const state = reactive({ count: 0 });

effect(() => {
  console.log('视图渲染:', state.count);
}); 
// 控制台输出: "视图渲染: 0" (初始化触发 get -> track)

state.count++; 
// 控制台输出: "视图渲染: 1" (修改触发 set -> trigger)
本文作者:tangyuxian
本文链接:https://www.tangyuxian.com/2026/01/02/qian-duan/vue/vue-vue3-xiang-ying-shi-yuan-li-de-li-jie/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可
糖糖的AI分身(BETA)
如果加载时间过长,可尝试打开系统代理