2025.09.04

职责分离:解耦依赖收集与更新派发

分离关注点:重构响应式系统的依赖收集与触发

🚀 重构的驱动力:单一职责原则

虽然我们目前的响应式系统已经具备了基本功能,但现有的 RefImpl 类承担了过多职责(管理数据、收集依赖、触发更新),这明显违反了单一职责原则 (Single Responsibility Principle)。

重构目标: 将依赖收集和更新触发逻辑拆分为独立模块,以实现:

  • ✅ 清晰的职责分离: 各模块只做一件事。
  • ✅ 高可维护性: 易于理解和修改。
  • ✅ 高扩展性: 为 computedreactive 等复杂功能做好铺垫。

原始实现:职责混杂的 RefImpl

在重构之前,RefImpl 像一个“全能选手”,直接包含了复杂的链表操作和更新逻辑,使得核心数据管理被细节代码淹没。

class RefImpl {
  // ... 链表头尾指针 subs / subsTail

  get value() {
    // ❌ 职责混杂点 1:在这里判断 activeSub 并调用 trackRef
    if (activeSub) {
      trackRef(this)
    }
    return this._value
  }

  set value(newValue) {
    this._value = newValue
    // ❌ 职责混杂点 2:在这里调用 triggerRef
    triggerRef(this)
  }
}

// trackRef 和 triggerRef 中包含了大量的链表操作细节...

重构:抽离核心逻辑到 system.ts

我们将依赖关系的建立 (track) 和更新的传播 (trigger) 视为一个独立的机制模块,将其抽离到 system.ts 中。

  1. system.ts: 依赖机制核心: 负责链表的管理和更新传播。
    // system.ts
    // ... 引入 Link 接口
    
    /**
     * 🔗 link:O(1) 建立依赖链表关系 (依赖收集)
     * @param dep 依赖项 (RefImpl 实例)
     * @param sub 订阅者 (activeSub / effect 函数)
     */
    export function link(dep, sub) {
      // ... 核心的双向链表尾部追加逻辑 (O(1))
    }
    
    /**
     * 📢 propagate:遍历链表并传播更新 (触发更新)
     * @param subs 订阅者链表的头节点
     */
    export function propagate(subs) {
      // ... 核心的链表遍历与 effect 收集/执行逻辑
    }
    
  2. effect.ts:副作用函数的执行与上下文 我们将副作用函数封装为 ReactiveEffect 类,专注于管理其执行上下文。
模块职责说明
activeSub全局变量, 用来追踪当前正在执行的 ReactiveEffect 实例
ReactiveEffect封窗 fn, 提供 run 方法来设置/清除 activeSub 上下文
// effect.ts
export let activeSub: ReactiveEffect | undefined

export class ReactiveEffect {
  constructor(public fn: Function) { /* ... */ }
  run(): any {
    activeSub = this // 📌 标记开始
    try {
      return this.fn() // 运行 fn,触发 getter/track
    }
    finally {
      activeSub = undefined // 🧹 标记结束
    }
  }
}

3.ref.ts: 职责回归纯粹 重构后的 ref.ts 只负责将外部的 trackReftriggerRef 调用转发给 system.ts 中的核心逻辑。

// ref.ts - 重构后
import { activeSub } from './effect'
import { link, propagate } from './system' // 引入核心机制

// ... RefImpl 类定义

export function trackRef(dep: RefImpl) {
  // 仅负责检查 activeSub,并将 dep/sub 转发给 system.link
  if (activeSub) {
    link(dep, activeSub)
  }
}

export function triggerRef(dep: RefImpl) {
  // 仅负责检查是否存在订阅者,并转发给 system.propagate
  if (dep.subs) {
    propagate(dep.subs)
  }
}

class RefImpl {
  // ...
  get value() {
    // 仅调用模块暴露的 API
    if (activeSub) {
      trackRef(this)
    }
    return this._value
  }

  set value(newValue: T) {
    this._value = newValue
    // 仅调用模块暴露的 API
    triggerRef(this)
  }
}

为什么将 effect 设计为类?

将副作用函数实现为 ReactiveEffect 类而非简单函数,是实现高级响应式功能的基石:

优势描述对应未来功能
状态封装类结构天然适合封装 effect 内部状态(如: 依赖了哪些数据、是否已经停止)依赖清理 (Cleanup),精确停止 (stop)。
行为扩展可以轻松为 effect 实例添加方法和属性自定义调度 (scheduler)、懒执行 (lazy)。
类型安全确保所有依赖项存储的都是具有完整生命周期管理的 ReactiveEffect 实例。

重构成果

通过这次重构,我们实现了核心关注点的清晰分离,让系统变得健壮和模块化:

模块职责依赖/关系
system.ts依赖关系管理专注于高效的链表操作 (link, propagate)。
effect.ts副作用函数上下文负责 ReactiveEffect 的生命周期和 activeSub 标记。
ref.ts响应式数据封装负责 RefImpl 数据管理,通过 trackRef/triggerRef 桥接。

这种模块化的设计不仅使代码更加清晰易读,还为未来的功能扩展和性能优化奠定了坚实的基础。