🎯 核心作用
packages/reactivity 是 Vue 3 的响应式系统核心包,它提供了,是 Vue 3 架构的基础。这个包是平台无关的,可以被任何 JavaScript 环境使用。
packages/reactivity 是 Vue 3 的响应式系统核心,它提供了完整的响应式数据管理能力:
-
响应式数据管理:reactive、ref、computed
-
副作用系统:effect、watch、watchEffect
-
依赖追踪:自动收集和触发更新 (dep)
-
作用域管理:EffectScope 生命周期管理 (effectScope)
-
类型安全:完整的 TypeScript 支持
这边简单实现reactive、effect、dep来理解Vue3的源码。
Reactive之代码实现
这边实现最最核心的将对象转化为响应式对象,实现了一个响应式系统的基础部分,包括:
- Proxy代理:使用ES6的Proxy对象来拦截对目标对象的操作
- get拦截器:
- 在读取属性时自动追踪依赖关系(track)
- 对嵌套对象进行递归代理,实现深层响应式
- set拦截器:
- 在设置属性时比较新旧值
- 只有值真正改变时才触发更新(trigger)
- 响应式原理:
- 通过代理模式实现对对象属性的访问和修改的拦截
- 结合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代码实现了响应式系统的依赖收集和触发代码:
- 数据结构:
targetMap: WeakMap,以原始对象为键,值为depsMapdepsMap: Map,以对象属性为键,值为dep集合dep: Set,存储所有依赖该属性的effect
- track函数:
- 建立target → key → effect的多级依赖关系
- 采用WeakMap避免内存泄漏
- 双向记录依赖关系(effect也记录自己依赖的dep)
- 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
Ref之代码实现
vue3中对象通过proxy实现响应式,但是基本类型是不能通过proxy实现响应式,这就有了ref的出现,其核心就是将基本类型包装成响应式对象。
核心机制详解:
类结构设计:
_value:内部存储的原始值(使用下划线约定表示私有)__v_isRef:类型标识(用于区分普通对象和ref)
关键特性:
- 值类型包装:可以对原始值(number/string等)实现响应式
- 惰性追踪:只有在effect中读取.value时才会建立依赖
- 变化检测:通过严格相等比较(!==)避免不必要的更新
与reactive的区别:
| 特性 | ref | reactive |
|---|---|---|
| 包装类型 | 原始值/对象 | 仅对象 |
| 访问方式 | 需要.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之代码实现
核心机制详解:
-
惰性求值原理:
- 通过
_dirty标志控制是否重新计算 - 首次访问时执行计算并缓存结果(
_value) - 依赖未变化时直接返回缓存值
- 通过
-
关键设计点:
- 双重缓存:
_value存储结果 +_dirty控制更新 - 智能调度:依赖变化时仅标记不立即计算
- 链式追踪:计算属性可嵌套其他响应式数据
- 双重缓存:
-
与普通ref的区别:
特性 computed ref 值来源 通过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++ // 触发控制台输出新计算结果
代码实际执行发生的事情
一、初始化阶段
-
创建
countrefconst count = ref(0)- 内部生成
RefImpl实例 targetMap尚无相关记录(此时还未被访问)
- 内部生成
-
**创建计算属性
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)
})
-
执行 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 }
-
-
**执行 getter
() => count.value * 2**:-
读取
count.value→ 触发count的get value()get value() { track(count, 'value') // 收集count的依赖 return count._value }
-
-
**
targetMap变化过程**:- 第一层依赖:
count→effect1(计算属性的内部effect) - 第二层依赖:
doubled→outerEffect(用户注册的effect)
- 第一层依赖:
三、触发更新阶段
count.value++ // 触发setter
-
**修改
count.value**:-
触发count的set value()
set value(newVal) { this._value = newVal trigger(count, 'value') // 通知依赖更新 }
-
-
**
trigger执行流程**:-
从
targetMap找到count对应的depsMap -
遍历
depsMap.get('value')中的所有 effect执行,[effect1]的effect1其实是computed实例内部的effect,因为有schedule,所以执行schedule() => { this._dirty = true // 标记计算属性为脏值 trigger(this, 'value') // 触发doubled的依赖更新 } -
触发
doubled的依赖(outerEffect)重新执行
-
-
**重新执行
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✅ 测试完成!')