Vue响应式系统的依赖收集核心逻辑track/trigger

发布于:2025-02-10 ⋅ 阅读:(56) ⋅ 点赞:(0)

响应式系统的依赖收集核心逻辑,主要用于管理“目标对象”(target)与“属性”(key)的依赖关系(Dep)。以下是 targetMapdepsMapdep 的关系,以及它们的作用和实际应用场景。


1. 关系分析

目标:
  • target(目标对象)与 key(对象的某个属性)之间的依赖关系映射起来,用于响应式更新。
三者的关系:
  1. targetMap:

    • 全局的依赖映射表。
    • Key: 响应式对象(target)。
    • Value: depsMap,存储该 target 的属性依赖。
  2. depsMap:

    • 针对某个具体 target 的属性依赖映射表。
    • Key: 属性名称(key)。
    • Value: dep,存储当前属性的依赖。
  3. dep:

    • 依赖集合。
    • 实际上是一个 Dep 实例,负责存储订阅当前属性的所有副作用(watchereffect)。
结构图:
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. 代码解读

核心逻辑:
  1. 检查全局依赖映射表是否有对应的 target(目标对象)。

    • 如果没有,创建一个新的 depsMap 并存储到 targetMap
  2. 检查 depsMap 中是否有对应的 key(属性)。

    • 如果没有,为该属性创建一个新的 Dep 实例,并存储到 depsMap
  3. 返回 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 某个属性的依赖集合,负责管理订阅的副作用。

网站公告

今日签到

点亮在社区的每一天
去签到