理解并尝试vue3源码的reactivity

133 阅读16分钟

🎯 核心作用

packages/reactivity 是 Vue 3 的响应式系统核心包,它提供了,是 Vue 3 架构的基础。这个包是平台无关的,可以被任何 JavaScript 环境使用。

packages/reactivity 是 Vue 3 的响应式系统核心,它提供了完整的响应式数据管理能力:

  1. 响应式数据管理:reactive、ref、computed

  2. 副作用系统:effect、watch、watchEffect

  3. 依赖追踪:自动收集和触发更新 (dep)

  4. 作用域管理:EffectScope 生命周期管理 (effectScope)

  5. 类型安全:完整的 TypeScript 支持

这边简单实现reactive、effect、dep来理解Vue3的源码。

Reactive之代码实现

这边实现最最核心的将对象转化为响应式对象,实现了一个响应式系统的基础部分,包括:

  1. Proxy代理‌:使用ES6的Proxy对象来拦截对目标对象的操作
  2. ‌get拦截器‌:
    • 在读取属性时自动追踪依赖关系(track)
    • 对嵌套对象进行递归代理,实现深层响应式
  3. ‌set拦截器‌:
    • 在设置属性时比较新旧值
    • 只有值真正改变时才触发更新(trigger)
  4. ‌响应式原理‌:
    • 通过代理模式实现对对象属性的访问和修改的拦截
    • 结合track和trigger函数实现依赖收集和派发更新
// 定义一个reactive函数,用于创建响应式对象
function reactive(obj) {
  // 返回一个新的Proxy代理对象
  return new Proxy(obj, {
    // 拦截属性读取操作
    get(target, key) {
      // 追踪依赖关系(通常在响应式系统中记录哪些组件依赖此属性)
      // TODO: 追踪依赖关系
      // track(target, key)
      // 获取目标对象的属性值
      const result = target[key]
      console.log('get', key, result)
      // 如果属性值是对象,则递归地将其转换为响应式对象
      // 这样可以实现深层响应式
      return typeof result === 'object' && result !== null
        ? reactive(result)
        : result
    },

    // 拦截属性设置操作
    set(target, key, value) {
      // 保存旧值用于比较
      const oldValue = target[key]
      // 设置新值
      target[key] = value
      // 如果新值与旧值不同,则触发更新
      if (value !== oldValue) {
        // TODO: 触发更新
        // trigger(target, key)
      }
      console.log('set', key, value)
      // 返回true表示设置成功
      return true
    }
  })
}


//  测试代码
const obj = reactive({
  name: 'John',
  age: 20,
  address: {
    city: 'Beijing',
    street: '123 Main St'
  }
})

console.log(obj.name)
obj.name = 'Jane'
console.log(obj.name)
obj.address.city = 'Shanghai'
console.log(obj.address.city)


注意这里的track和trigger属于dep的内容,后面实现。

执行node self-reactive.js,get和set的内容都被打印了~

Dep之代码实现

Dep代码实现了响应式系统的依赖收集和触发代码:

  1. 数据结构‌:
    • targetMap: WeakMap,以原始对象为键,值为depsMap
    • depsMap: Map,以对象属性为键,值为dep集合
    • dep: Set,存储所有依赖该属性的effect
  2. track函数‌:
    • 建立target → key → effect的多级依赖关系
    • 采用WeakMap避免内存泄漏
    • 双向记录依赖关系(effect也记录自己依赖的dep)
  3. trigger函数‌:
    • 根据target和key找到所有依赖的effect
    • 支持自定义调度器(scheduler)
    • 避免当前正在执行的effect重复触发
// 全局变量,存储所有响应式对象的依赖关系,键是响应式对象,值是该对象所有key的依赖关系
//「targetMap」 的结构如下:
// {
//   target1: {
//     key1: [effect1, effect2],
//     key2: [effect3]
//   },
//   target2: {
//     key3: [effect4]
//   }
// }
const targetMap = new WeakMap()

// 当前正在执行的effect
let activeEffect = null

// 依赖收集函数
function track(target, key) {
  // 如果没有活跃的effect,不需要收集
  if (!activeEffect) return

  // 获取target对应的depsMap(存储该对象所有key的依赖)
  // 举个例子,「depsMap」 的结构如下:
  // {
  //   key1: [effect1, effect2],
  //   key2: [effect3]
  // }
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    // 如果没有则新建一个Map并存入targetMap
    targetMap.set(target, (depsMap = new Map()))
  }

  // 获取key对应的依赖集合(存储所有依赖此key的effect)
  // 举个例子,「dep」 的结构如下:
  //  [effect1, effect2]
  let dep = depsMap.get(key)
  if (!dep) {
    // 如果没有则新建一个Set并存入depsMap
    depsMap.set(key, (dep = new Set()))
  }
  if(dep.has(activeEffect)) {
    return
  }
  // 将当前活跃effect添加到依赖集合
  dep.add(activeEffect)
  // effect也记录自己依赖的所有dep集合(用于清理)
  // 举个例子,「activeEffect.deps」 的结构如下:
  // [dep1, dep2],其中dep1和dep2是依赖此key的effect
  // [[effect1, effect2], [effect3]]
  activeEffect.deps.push(dep)
}

// 触发更新函数
function trigger(target, key) {
  // 获取target对应的所有key依赖
  const depsMap = targetMap.get(target)
  if (!depsMap) return

  // 获取该key对应的所有effect
  const dep = depsMap.get(key)
  if (dep) {
    // 遍历执行所有依赖此key的effect
    dep.forEach(effect => {
      // 避免当前正在执行的effect重复触发
      if (effect !== activeEffect) {
        if (effect.scheduler) {
          // 如果有调度器则使用调度器执行
          effect.scheduler()
        } else {
          // 否则直接运行effect
          effect.run()
        }
      }
    })
  }
}

Effect之代码实现

ReactiveEffect 类‌:

  • 每个effect实例对应一个需要响应式执行的函数

  • 通过deps数组维护与所有Dep集合的双向引用

  • 提供run()方法实现依赖收集的上下文管理

关键设计点‌:

  • 活性标记‌:active控制是否参与依赖收集
  • 清理机制‌:每次执行前清理旧依赖,避免无效引用
  • 错误隔离‌:try-finally确保always清理上下文
// 响应式副作用类(核心调度单元)
class ReactiveEffect {
  constructor(fn) {
    this.fn = fn        // 原始副作用函数
    this.deps = []      // 依赖集合数组(用于双向绑定)
    this.active = true  // 激活状态标记
  }

  // 执行副作用函数
  run() {
    if (!this.active) return this.fn() // 非激活状态直接执行

    try {
      activeEffect = this  // 设置当前活跃effect
      console.log('🔄 执行副作用...')
      return this.fn()     // 执行原始函数(期间会触发track)
    } finally {
      activeEffect = null  // 执行完成后清除引用
      
    }
  }

  // 停止响应式追踪
  stop() {
    this.active = false  // 标记为非激活状态
    this.cleanup()       // 清理所有依赖关系
  }

  // 清理依赖关系(双向解除绑定)
  cleanup() {
    this.deps.forEach(dep => dep.delete(this)) // 从所有dep中移除自己
    this.deps.length = 0  // 清空依赖数组
  }
}

// 创建并运行副作用的工厂函数
function effect(fn) {
  const _effect = new ReactiveEffect(fn) // 创建effect实例
  _effect.run()                         // 立即执行首次收集依赖
  return _effect                        // 返回可管理的effect对象
}

三小只合体 - Reactivity

将以上代码合体,简单的reactive系统初步形成!

// ==================== 完整的响应式系统实现 ====================

// ==================== 依赖追踪系统 (dep.js) ====================
// 全局变量,存储所有响应式对象的依赖关系
const targetMap = new WeakMap()

// 当前正在执行的effect
let activeEffect = null

// 设置当前活跃的 effect(供外部使用)
function setActiveEffect(effect) {
  activeEffect = effect
}

// 依赖收集函数
function track(target, key) {
  // 如果没有活跃的effect,不需要收集
  if (!activeEffect) return

  // 获取target对应的depsMap(存储该对象所有key的依赖)
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    // 如果没有则新建一个Map并存入targetMap
    targetMap.set(target, (depsMap = new Map()))
  }

  // 获取key对应的依赖集合(存储所有依赖此key的effect)
  let dep = depsMap.get(key)
  if (!dep) {
    // 如果没有则新建一个Set并存入depsMap
    depsMap.set(key, (dep = new Set()))
  }
  if (dep.has(activeEffect)) {
    return
  }
  // 将当前活跃effect添加到依赖集合
  dep.add(activeEffect)
  // effect也记录自己依赖的所有dep集合(用于清理)
  activeEffect.deps.push(dep)

}

// 触发更新函数
function trigger(target, key) {
  // 获取target对应的所有key依赖
  const depsMap = targetMap.get(target)
  if (!depsMap) return

  // 获取该key对应的所有effect
  const dep = depsMap.get(key)
  if (dep) {
    console.log('⚡ 触发更新:', key, '-> 执行', dep.size, '个副作用')
    // 遍历执行所有依赖此key的effect
    dep.forEach(effect => {
      // 避免当前正在执行的effect重复触发
      if (effect !== activeEffect) {
        if (effect.scheduler) {
          // 如果有调度器则使用调度器执行
          effect.scheduler()
        } else {
          // 否则直接运行effect
          effect.run()
        }
      }
    })
  }
}

// ==================== 响应式对象系统 (reactive.js) ====================
// 定义一个reactive函数,用于创建响应式对象
function reactive(obj) {
  // 返回一个新的Proxy代理对象
  return new Proxy(obj, {
    // 拦截属性读取操作
    get(target, key) {
      // 追踪依赖关系(通常在响应式系统中记录哪些组件依赖此属性)
      track(target, key)
      // 获取目标对象的属性值
      const result = target[key]
      // 如果属性值是对象,则递归地将其转换为响应式对象
      // 这样可以实现深层响应式
      return typeof result === 'object' && result !== null
        ? reactive(result)
        : result
    },

    // 拦截属性设置操作
    set(target, key, value) {
      // 保存旧值用于比较
      const oldValue = target[key]
      // 设置新值
      target[key] = value
      // 如果新值与旧值不同,则触发更新
      if (value !== oldValue) {
        // 触发更新
        trigger(target, key)
      }
      // 返回true表示设置成功
      return true
    }
  })
}

// ==================== 副作用系统 (effect.js) ====================
// 响应式副作用类(核心调度单元)
class ReactiveEffect {
  constructor(fn) {
    this.fn = fn        // 原始副作用函数
    this.deps = []      // 依赖集合数组(用于双向绑定)
    this.active = true  // 激活状态标记
  }

  // 执行副作用函数
  run() {
    if (!this.active) return this.fn() // 非激活状态直接执行

    try {
      setActiveEffect(this)  // 设置当前活跃effect
      console.log('🔄 执行effect函数...')
      return this.fn()     // 执行原始函数(期间会触发track)
    } finally {
      setActiveEffect(null)  // 执行完成后清除引用
      // 注意:这里不应该调用 cleanup(),因为会清空依赖关系
      // 只有在 stop() 时才应该清理依赖
    }
  }

  // 停止响应式追踪
  stop() {
    this.active = false  // 标记为非激活状态
    this.cleanup()       // 清理所有依赖关系
  }

  // 清理依赖关系(双向解除绑定)
  cleanup() {
    this.deps.forEach(dep => dep.delete(this)) // 从所有dep中移除自己
    this.deps.length = 0  // 清空依赖数组
  }
}

// 创建并运行副作用的工厂函数
function effect(fn) {
  const _effect = new ReactiveEffect(fn) // 创建effect实例
  _effect.run()                         // 立即执行首次收集依赖
  return _effect                        // 返回可管理的effect对象
}

// ==================== 测试代码 ====================
console.log('🚀 开始测试响应式系统...\n')

const obj = reactive({ count: 0, name: 'John' })

// 创建响应式effect
const myEffect = effect(() => {
  console.log('📊 Count:', obj.count)
})
const myEffect2 = effect(() => {
  console.log('📊 Count2:', obj.count)
})
const myEffect1 = effect(() => {
  console.log('👤 Name:', obj.name)
})

// 修改触发更新
obj.count++
obj.count++
obj.name = 'Jane1'
obj.name = 'Jane2'

// 手动停止响应
myEffect.stop()

obj.count = 100

console.log('\n✅ 测试完成!')


执行

node xx.js

reactive.png

Ref之代码实现

vue3中对象通过proxy实现响应式,但是基本类型是不能通过proxy实现响应式,这就有了ref的出现,其核心就是将基本类型包装成响应式对象。

核心机制详解:

类结构设计‌:

  • _value:内部存储的原始值(使用下划线约定表示私有)
  • __v_isRef:类型标识(用于区分普通对象和ref)

关键特性‌:

  • 值类型包装‌:可以对原始值(number/string等)实现响应式
  • 惰性追踪‌:只有在effect中读取.value时才会建立依赖
  • 变化检测‌:通过严格相等比较(!==)避免不必要的更新

与reactive的区别‌:

特性refreactive
包装类型原始值/对象仅对象
访问方式需要.value访问直接属性访问
模板解包自动解包无需特殊处理

‌代码实现:

/**
 * Ref 引用类型的实现类
 * 用于创建具有响应式能力的值引用
 */
class RefImpl {
  constructor(value) {
    this._value = value       // 内部存储的实际值
    this.__v_isRef = true     // 标识为ref类型(用于运行时检查)
  }
​
  /**
   * value属性的getter拦截器
   * 在读取值时触发依赖收集
   */
  get value() {
    track(this, 'value')                // 追踪依赖(建立effect与当前ref的关系)
    return this._value                  // 返回实际值
  }
​
  /**
   * value属性的setter拦截器
   * 在修改值时触发依赖更新
   */
  set value(newVal) {
    if (newVal !== this._value) {       // 值变化时才触发更新
      this._value = newVal              // 更新内部值
      trigger(this, 'value')            // 触发依赖更新(通知所有关联effect)
    }
  }
}
​
/**
 * 创建响应式引用的工厂函数
 * @param {any} value - 需要响应式包装的值
 * @returns {RefImpl} 响应式引用对象
 */
function ref(value) {
  return new RefImpl(value)  // 返回ref实例
}

Computed之代码实现

核心机制详解:

  1. 惰性求值原理‌:

    • 通过 _dirty 标志控制是否重新计算
    • 首次访问时执行计算并缓存结果(_value
    • 依赖未变化时直接返回缓存值
  2. ‌‌关键设计点‌:

    • 双重缓存‌:_value存储结果 + _dirty控制更新
    • 智能调度‌:依赖变化时仅标记不立即计算
    • 链式追踪‌:计算属性可嵌套其他响应式数据
  3. 与普通ref的区别‌:

    特性computedref
    值来源通过getter函数计算直接赋值
    更新时机依赖变化时标记直接赋值时触发
    性能优化自动缓存计算结果无缓存

代码实现:

/**
 * 计算属性的响应式实现类
 * 基于getter函数实现惰性求值和缓存优化
 */
class ComputedRefImpl {
  constructor(getter) {
    this._dirty = true      // 脏检查标志(控制是否需要重新计算)
    this._value = undefined // 缓存的计算结果
    this._getter = getter   // 计算函数
    this.__v_isRef = true   // 标识为ref类型
​
    // 创建响应式effect(核心机制)
    this.effect = new ReactiveEffect(
      getter,
      // 调度器函数(当依赖变化时触发)
      () => {
        this._dirty = true            // 标记为需要重新计算
        trigger(this, 'value')        // 触发value属性的依赖更新
      }
    )
  }
​
  /**
   * value属性的getter拦截器
   * 实现计算属性的惰性求值和缓存机制
   */
  get value() {
    if (this._dirty) {
      // 执行计算函数并缓存结果
      this._value = this.effect.run() 
      this._dirty = false // 重置脏检查标志
    }
    track(this, 'value')  // 追踪当前读取操作
    return this._value    // 返回缓存值
  }
}
​
/**
 * 创建计算属性的工厂函数
 * @param {Function} getter - 计算函数
 * @returns {ComputedRefImpl} 计算属性引用
 */
function computed(getter) {
  return new ComputedRefImpl(getter)
}
​

深度理解computed的实现

其实我第一次捋,捋了很久,computed内部使用effect很精妙啊!

拿个简单的例子看下:

const count = ref(0)
​
// 创建计算属性
const doubled = computed(() => count.value * 2)
​
// 自动追踪依赖
effect(() => {
  console.log('双倍值:', doubled.value)
})
​
count.value++ // 触发控制台输出新计算结果

代码实际执行发生的事情

一、初始化阶段

  1. 创建 count ref

    const count = ref(0)
    
    • 内部生成 RefImpl 实例
    • targetMap 尚无相关记录(此时还未被访问)
  2. ‌**创建计算属性 doubled**‌

    const doubled = computed(() => count.value * 2)
    
    • 实例化ComputedRefImpl

      • 初始化 _dirty = true

      • 创建ReactiveEffect

        this.effect = new ReactiveEffect(
          () => count.value * 2, // getter
          () => { this._dirty = true; trigger(this, 'value') } // scheduler
        )
        
    • 此时 targetMap 仍为空‌(getter 尚未执行)

二、effect 注册阶段

effect(() => {
  console.log('双倍值:', doubled.value)
})
  1. 执行 effect 回调‌:

    • 首次读取doubled.value→ 触发计算属性的get value()

      get value() {
        if (this._dirty) {
          this._value = this.effect.run() // 执行getter
          this._dirty = false
        }
        track(this, 'value') // 追踪当前effect
        return this._value
      }
      
  2. ‌**执行 getter () => count.value * 2**‌:

    • 读取 count.value→ 触发count的get value()

      get value() {
        track(count, 'value') // 收集count的依赖
        return count._value
      }
      
  3. ‌**targetMap 变化过程**‌:

    • 第一层依赖‌:counteffect1(计算属性的内部effect)
    • 第二层依赖‌:doubledouterEffect(用户注册的effect)

三、触发更新阶段

count.value++ // 触发setter
  1. ‌**修改 count.value**‌:

    • 触发count的set value()

      set value(newVal) {
        this._value = newVal
        trigger(count, 'value') // 通知依赖更新
      }
      
  2. ‌**trigger 执行流程**‌:

    • targetMap 找到 count 对应的 depsMap

    • 遍历depsMap.get('value')中的所有 effect执行,[effect1]的effect1其实是computed实例内部的effect,因为有schedule,所以执行schedule

      () => {
        this._dirty = true      // 标记计算属性为脏值
        trigger(this, 'value')  // 触发doubled的依赖更新
      }
      
    • 触发 doubled 的依赖(outerEffect)重新执行

  3. ‌**重新执行 outerEffect**‌:

    • 再次读取 doubled.value → 发现 _dirty=true → 重新计算 count.value * 2
    • 输出最新结果:双倍值: 2

四、关键数据结构快照

更新后的 targetMap

{
  // 原始ref的依赖
  count: {
    'value': [ effect1 ] // computed的内部effect
  },
  // 计算属性的依赖
  doubled: {
    'value': [ outerEffect ] // 用户的console.log effect
  }
}

新demo

// import { generateDrawioFile } from './generate-drawio.js';
// ==================== 完整的响应式系统实现 ====================// ==================== 依赖追踪系统 (dep.js) ====================
// 全局变量,存储所有响应式对象的依赖关系
const targetMap = new WeakMap()
​
// 当前正在执行的effect
let activeEffect = null// 设置当前活跃的 effect(供外部使用)
function setActiveEffect(effect) {
  activeEffect = effect
}
​
// 依赖收集函数
function track(target, key) {
  // 如果没有活跃的effect,不需要收集
  if (!activeEffect) return
​
  // 获取target对应的depsMap(存储该对象所有key的依赖)
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    // 如果没有则新建一个Map并存入targetMap
    targetMap.set(target, (depsMap = new Map()))
  }
​
  // 获取key对应的依赖集合(存储所有依赖此key的effect)
  let dep = depsMap.get(key)
  if (!dep) {
    // 如果没有则新建一个Set并存入depsMap
    depsMap.set(key, (dep = new Set()))
  }
  if (dep.has(activeEffect)) {
    return
  }
  // 将当前活跃effect添加到依赖集合
  dep.add(activeEffect)
  // effect也记录自己依赖的所有dep集合(用于清理)
  activeEffect.deps.push(dep)
​
}
​
// 触发更新函数
function trigger(target, key) {
  // 获取target对应的所有key依赖
  const depsMap = targetMap.get(target)
  if (!depsMap) return
​
  // 获取该key对应的所有effect
  const dep = depsMap.get(key)
  if (dep) {
    console.log('⚡ 触发更新:', key, '-> 执行', dep.size, '个副作用')
    // 遍历执行所有依赖此key的effect
    dep.forEach(effect => {
      // 避免当前正在执行的effect重复触发
      if (effect !== activeEffect) {
        if (effect.scheduler) {
          // 如果有调度器则使用调度器执行
          effect.scheduler()
        } else {
          // 否则直接运行effect
          effect.run()
        }
      }
    })
  }
}
​
// ==================== 响应式对象系统 (reactive.js) ====================
// 定义一个reactive函数,用于创建响应式对象
function reactive(obj) {
  // 返回一个新的Proxy代理对象
  return new Proxy(obj, {
    // 拦截属性读取操作
    get(target, key) {
      // 追踪依赖关系(通常在响应式系统中记录哪些组件依赖此属性)
      track(target, key)
      // 获取目标对象的属性值
      const result = target[key]
      // 如果属性值是对象,则递归地将其转换为响应式对象
      // 这样可以实现深层响应式
      return typeof result === 'object' && result !== null
        ? reactive(result)
        : result
    },
​
    // 拦截属性设置操作
    set(target, key, value) {
      // 保存旧值用于比较
      const oldValue = target[key]
      // 设置新值
      target[key] = value
      // 如果新值与旧值不同,则触发更新
      if (value !== oldValue) {
        // 触发更新
        trigger(target, key)
      }
      // 返回true表示设置成功
      return true
    }
  })
}
​
// ==================== Ref 系统 (ref.js) ====================
// Ref 实现类 - 用于包装基本类型值
class RefImpl {
  constructor(value) {
    this._value = value
    this.__v_isRef = true
  }
​
  get value() {
    track(this, 'value')
    return this._value
  }
​
  set value(newVal) {
    if (newVal !== this._value) {
      this._value = newVal
      trigger(this, 'value')
    }
  }
}
​
// 创建 ref 的工厂函数
function ref(value) {
  return new RefImpl(value)
}
​
// ==================== 计算属性系统 (computed.js) ====================
// 计算属性实现类
class ComputedRefImpl {
  constructor(getter) {
    this._dirty = true
    this._value = undefined
    this._getter = getter
    this.__v_isRef = true
​
    // computed(()=>{return firstName.value + lastName.value})
    // 创建内部 effect,用于追踪依赖
    this.effect = new ReactiveEffect(getter, () => {
      // 当依赖变化时,标记为脏数据
      if (!this._dirty) {
        this._dirty = true
        trigger(this, 'value')
      }
    })
  }
​
  get value() {
    // 如果是脏数据,重新计算
    if (this._dirty) {
      this._value = this.effect.run()
      this._dirty = false
    }
    // 收集依赖
    track(this, 'value')
    return this._value
  }
}
​
// 创建计算属性的工厂函数
function computed(getter) {
  return new ComputedRefImpl(getter)
}
​
// ==================== 副作用系统 (effect.js) ====================
// 响应式副作用类(核心调度单元)
class ReactiveEffect {
  constructor(fn, scheduler = null) {
    this.fn = fn        // 原始副作用函数
    this.scheduler = scheduler  // 调度器函数
    this.deps = []      // 依赖集合数组(用于双向绑定)
    this.active = true  // 激活状态标记
  }
​
  // 执行副作用函数
  run() {
    if (!this.active) return this.fn() // 非激活状态直接执行
​
    try {
      setActiveEffect(this)  // 设置当前活跃effect
      console.log('🔄 执行effect函数...')
      return this.fn()     // 执行原始函数(期间会触发track)
    } finally {
      setActiveEffect(null)  // 执行完成后清除引用
      // 注意:这里不应该调用 cleanup(),因为会清空依赖关系
      // 只有在 stop() 时才应该清理依赖
    }
  }
​
  // 停止响应式追踪
  stop() {
    this.active = false  // 标记为非激活状态
    this.cleanup()       // 清理所有依赖关系
  }
​
  // 清理依赖关系(双向解除绑定)
  cleanup() {
    this.deps.forEach(dep => dep.delete(this)) // 从所有dep中移除自己
    this.deps.length = 0  // 清空依赖数组
  }
}
​
// 创建并运行副作用的工厂函数
function effect(fn) {
  const _effect = new ReactiveEffect(fn) // 创建effect实例
  _effect.run()                         // 立即执行首次收集依赖
  return _effect                        // 返回可管理的effect对象
}
​
// ==================== 测试代码 ====================
console.log('🚀 开始测试响应式系统...\n')
​
// 自动生成 Draw.io 图表
// try {
//   const drawioPath = generateDrawioFile();
//   if (drawioPath) {
//     console.log('📊 响应式系统架构图已生成,可在 Draw.io 中查看\n');
//   }
// } catch (error) {
//   console.log('⚠️  Draw.io 图表生成失败,但不影响测试运行\n');
// }// ==================== 测试 Reactive ====================
console.log('1️⃣ 测试 Reactive 对象:')
const obj = reactive({ count: 0, name: 'John' })
​
// 创建响应式effect
const myEffect = effect(() => {
  console.log('📊 Count:', obj.count)
})
const myEffect2 = effect(() => {
  console.log('📊 Count2:', obj.count)
})
const myEffect1 = effect(() => {
  console.log('👤 Name:', obj.name)
})
​
// 修改触发更新
obj.count++
obj.count++
obj.name = 'Jane1'
obj.name = 'Jane2'// 手动停止响应
myEffect.stop()
​
obj.count = 100
​
console.log('\n' + '='.repeat(50) + '\n')
​
// ==================== 测试 Ref ====================
console.log('2️⃣ 测试 Ref 系统:')
const count = ref(0)
const name = ref('Alice')
const price = ref(100)
​
// 创建响应式effect
const refEffect1 = effect(() => {
  console.log('💰 Price:', price.value)
})
const refEffect2 = effect(() => {
  console.log('📊 Count:', count.value)
})
const refEffect4 = effect(() => {
  console.log('📊 Count4:', count.value)
})
const refEffect3 = effect(() => {
  console.log('👤 Name:', name.value)
})
​
// 修改 ref 值
count.value = 5
count.value = 10
name.value = 'Bob'
price.value = 200// 手动停止响应
refEffect1.stop()
​
// 再次修改(不会触发已停止的 effect)
price.value = 300
​
console.log('\n' + '='.repeat(50) + '\n')
​
// ==================== 测试 Computed ====================
console.log('3️⃣ 测试 Computed 计算属性:')
const firstName = ref('John')
const lastName = ref('Doe')
const age = ref(25)
​
// 创建计算属性
const fullName = computed(() => {
  console.log('🔄 计算 fullName...')
  return `${firstName.value} ${lastName.value}`
})
​
const displayInfo = computed(() => {
  console.log('🔄 计算 displayInfo...')
  return `${fullName.value}, Age: ${age.value}`
})
​
const isAdult = computed(() => {
  console.log('🔄 计算 isAdult...')
  return age.value >= 18
})
​
// 创建响应式effect
const computedEffect1 = effect(() => {
  console.log('👤 Full Name:', fullName.value)
})
const computedEffect2 = effect(() => {
  console.log('📋 Display Info:', displayInfo.value)
})
const computedEffect3 = effect(() => {
  console.log('🎂 Is Adult:', isAdult.value)
})
​
// 修改依赖数据
console.log('\n🔄 修改 firstName:')
firstName.value = 'Jane'
​
console.log('\n🔄 修改 lastName:')
lastName.value = 'Smith'
​
console.log('\n🔄 修改 age:')
age.value = 30// 手动停止响应
computedEffect1.stop()
​
// 再次修改(不会触发已停止的 effect)
firstName.value = 'Mike'
​
console.log('\n✅ 测试完成!')
​
​
​
​
​
​