Vue 3 响应式系统可视化流程

6 阅读4分钟

🎯 核心架构概览

整体数据流向图

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   用户访问数据    │───▶│   Proxy拦截      │───▶│   依赖收集(track) │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Dep容器创建    │◀───│   创建Link链接    │◀───│   Subscriber记录  │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   用户修改数据    │───▶│   Proxy拦截      │───▶│   触发更新(trigger)│
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   版本号递增     │───▶│   通知订阅者      │───▶│   批处理执行      │
└─────────────────┘    └─────────────────┘    └─────────────────┘

🧩 核心组件可视化

1. 双向链表结构

Subscriber (Effect/Computed) 链表结构:

头部 [Link1][Link2][Link3] ⇄ ... ⇄ [LinkN] 尾部
      ↑                                          ↑
   deps指针                                   depsTail指针

每个Link同时存在于两个链表中:

Subscriber deps链表:  Effect → Link1 → Link2 → Link3
                             ↖_______↗
Dep subs链表:        Dep → Link2 → Link1 → Link3
                             ↖________↗

2. 依赖追踪过程

访问属性时的依赖收集流程:

step 1: activeSub = currentEffect
step 2: Proxy.get() 拦截
step 3: track(target, key)
        ↓
step 4: targetMap.get(target) 获取 depsMap
        ↓
step 5: depsMap.get(key) 获取 dep
        ↓
step 6: dep.track() 创建/复用 Link
        ↓
step 7: 建立双向链接关系
        Effect.deps ⇄ Link ⇄ Dep.subs

3. 更新触发过程

修改属性时的更新流程:

step 1: Proxy.set() 拦截
step 2: trigger(target, key)
        ↓
step 3: depsMap.get(key) 获取 dep
        ↓
step 4: dep.version++ (版本递增)
step 5: dep.notify() 通知所有订阅者
        ↓
step 6: 批处理调度
        for each subscriber in dep.subs:
            subscriber.notify() → 加入批处理队列
        ↓
step 7: endBatch() 执行所有副作用

📊 核心API工作原理图示

reactive() 工作流程

reactive(obj) 创建过程:

输入对象 obj
    ↓
检查是否已存在代理 ←─────────────────┐
    ↓ 否                          │
获取对象类型 [COMMON/COLLECTION]    │
    ↓                              │
选择对应处理器                     │
    ↓                              │
new Proxy(obj, handlers)           │
    ↓                              │
proxyMap.set(obj, proxy) ──────────┘
    ↓
返回 proxy 对象

ref() 内部结构

ref(value) 结构示意图:

┌─────────────────────────────┐
│        RefImpl 实例          │
├─────────────────────────────┤
│ _value: T                   │ ◀─ 处理后的值
│ _rawValue: T                │ ◀─ 原始值
│ dep: Dep                    │ ◀─ 独立的依赖容器
│ [IS_REF]: true              │
│ [IS_SHALLOW]: boolean       │
├─────────────────────────────┤
│ get value() {               │
│   this.dep.track()          │ ◀─ 依赖收集
│   return this._value        │
│ }                           │
│ set value(newVal) {         │
│   // 值比较和更新逻辑        │this.dep.trigger()        │ ◀─ 触发更新
│ }                           │
└─────────────────────────────┘

computed() 执行机制

computed(getter) 工作流程:

┌─────────────────────────────────────┐
│         ComputedRefImpl             │
├─────────────────────────────────────┤
│ _value: any                         │
│ dep: Dep (自身依赖)                  │
│ deps: Link (依赖的其他响应式数据)     │
│ flags: DIRTY/TRACKING/EVALUATED     │
└─────────────────────────────────────┘
          │
    get value() 时
          ↓
┌─────────────────────────────────────┐
│ 1. this.dep.track()                 │ ◀─ 收集对computed的依赖
│ 2. refreshComputed(this)            │ ◀─ 刷新计算值
│ 3. return this._value               │
└─────────────────────────────────────┘
          │
   refreshComputed() 流程
          ↓
┌─────────────────────────────────────┐
│ 检查flags和globalVersion快速路径     │
│ ↓                                   │
│ 准备依赖追踪 prepareDeps()          │
│ ↓                                   │
│ 执行getter函数                      │
│ ↓                                   │
│ 比较新旧值,必要时更新 _value        │
│ ↓                                   │
│ 清理未使用依赖 cleanupDeps()        │
└─────────────────────────────────────┘

🔧 副作用管理可视化

Effect执行生命周期

ReactiveEffect.run() 完整流程:

开始执行
    ↓
设置 RUNNING 状态
    ↓
cleanupEffect() ──┐
    ↓             │ 清理上轮副作用
prepareDeps() ◄───┘
    ↓
切换上下文: activeSub = this
    ↓
执行用户函数 fn() ──┐
    ↓              │ 此时进行依赖收集
cleanupDeps() ◄────┘
    ↓
恢复上下文
    ↓
清除 RUNNING 状态
    ↓
完成执行

依赖清理机制

cleanupDeps() 工作原理:

初始状态:
Effect deps链表: [Link1(v=3)][Link2(v=-1)][Link3(v=2)]

执行过程:
1. 所有Link.version设为-1 (prepareDeps阶段)
2. 执行过程中使用的Link同步为当前dep.version
3. cleanupDeps检查:
   - Link1: version=3 ≠ -1 → 保留 ✓
   - Link2: version=-1 → 清理 ✗
   - Link3: version=2 ≠ -1 → 保留 ✓

最终状态:
Effect deps链表: [Link1][Link3]

⚡ 性能优化策略图示

批处理机制

多个更新的批处理流程:

更新1: depA.trigger() ──┐
更新2: depB.trigger() ──┼──▶ 加入批处理队列
更新3: depC.trigger() ──┘
        ↓
    startBatch()
        ↓
检查 batchedSub 队列
        ↓
逆序执行所有副作用
Effect3.run() ← depC
Effect1.run() ← depA  
Effect2.run() ← depB
        ↓
    endBatch()

版本号优化

全局版本号优化机制:

全局版本: globalVersion = 5

ComputedA (globalVersion = 5) ──┐
ComputedB (globalVersion = 3) ──┼──▶ refreshComputed检查
ComputedC (globalVersion = 5) ──┘

执行流程:
1. 检查 globalVersion 是否匹配
2. ComputedA: 5 === 5 → 跳过 ✓
3. ComputedB: 3 !== 5 → 需要刷新 ✗
4. ComputedC: 5 === 5 → 跳过 ✓

🎯 响应式对象代理策略

Proxy Handler 拦截点

对象属性访问拦截:

get(target, key, receiver):
├── 特殊标志位处理 (SKIP, IS_REACTIVE等)
├── 数组方法拦截 (includes, indexOf等)
├── 依赖收集 track(target, key)
├── ref自动解包
└── 嵌套对象递归代理

set(target, key, value, receiver):
├── 值预处理 (toRaw转换)
├── ref赋值特殊处理
├── Reflect.set执行设置
└── 条件触发 trigger(target, key)

集合类型特殊处理

Map/Set 集合处理器:

Map.get(key):
├── key 和 rawKey 双重追踪
├── 值的响应式包装
└── 嵌套处理

Map.set(key, value):
├── 值预处理
├── 检查是否存在key
├── Reflect.set执行
└── 根据情况触发ADD/SET

size/getter:
└── 追踪ITERATE_KEY依赖

📈 运行时交互示例

完整更新循环示例

示例代码执行过程:

const state = reactive({ count: 0 })
const double = computed(() => state.count * 2)
watch(double, (newVal) => console.log(newVal))

// 第一次访问
console.log(double.value)
↓
1. double.get() → refreshComputed
2. 执行 () => state.count * 2
3. 访问 state.count → track收集依赖
4. 建立 double → state.count 的依赖关系
5. 返回 0

// 修改数据
state.count = 11. Proxy.set拦截
2. trigger(state, 'count')
3. 通知double的dep
4. double标记为DIRTY
5. batch(double)
6. endBatch执行double的effect
7. 重新计算double.value = 2
8. 触发watch回调打印2

内存管理示例

依赖关系自动清理:

初始状态:
Effect deps: [state.count][state.name][state.age]

执行条件渲染:
if (showName) {
  console.log(state.name)  // 只访问name
}

下次更新:
1. prepareDeps: 所有version = -1
2. 执行effect: 只访问state.name
3. cleanupDeps:
   - state.count: version = -1 → 清理
   - state.name: version = current → 保留
   - state.age: version = -1 → 清理

结果:
Effect deps: [state.name]  // 自动清理未使用依赖

🎨 可视化最佳实践

设计模式总结

Vue 3响应式系统的三大核心模式:

1. 观察者模式 (Observer Pattern)
   Subject(Observable) ←→ Observer
   Dep ←→ Subscriber(Effect/Computed)

2. 代理模式 (Proxy Pattern)  
   RealSubject ← Proxy ← Client
   Target ← Proxy ← User Code

3. 发布-订阅模式 (Pub-Sub Pattern)
   Publisher → Event Channel → Subscriber
   trigger() → batch queue → run()

优势:
✅ 解耦合: 数据变化与副作用执行分离
✅ 精确更新: 只更新真正受影响的部分
✅ 自动管理: 依赖关系自动建立和清理
✅ 性能优化: 批处理和版本号快速路径