-
Couldn't load subscription status.
- Fork 1
Home
YanqingXu edited this page Jun 28, 2025
·
1 revision
Alien Signals 采用了基于推拉混合模型的响应式架构:
┌─────────────────────────────────────────────────────────────┐
│ 响应式系统架构 │
├─────────────────────────────────────────────────────────────┤
│ 应用层 │ Signal │ Computed │ Effect │ EffectScope │
├─────────────────────────────────────────────────────────────┤
│ 调度层 │ 批量更新 │ 队列管理 │ 脏值检查 │ 循环依赖检测 │
├─────────────────────────────────────────────────────────────┤
│ 存储层 │ 双向链表 │ 位运算标记 │ 全局状态 │ 依赖图管理 │
└─────────────────────────────────────────────────────────────┘
- 零配置依赖追踪: 通过函数调用自动建立依赖关系
- 最小化重新计算: 只有真正需要时才重新计算
- 内存效率: 自动清理不再使用的依赖关系
- 性能优先: 使用位运算和链表优化关键路径
每个响应式对象都包含以下核心字段:
-- Signal 结构
{
value = any, -- 当前值
previousValue = any, -- 上一次的值(用于变化检测)
subs = Link, -- 订阅者链表头
subsTail = Link, -- 订阅者链表尾
flags = number -- 状态标记位
}
-- Computed 结构
{
value = any, -- 缓存的计算结果
getter = function, -- 计算函数
subs = Link, -- 订阅者链表头
subsTail = Link, -- 订阅者链表尾
deps = Link, -- 依赖链表头
depsTail = Link, -- 依赖链表尾
flags = number -- 状态标记位
}
-- Effect 结构
{
fn = function, -- 副作用函数
subs = Link, -- 子副作用链表头
subsTail = Link, -- 子副作用链表尾
deps = Link, -- 依赖链表头
depsTail = Link, -- 依赖链表尾
flags = number -- 状态标记位
}双向链表的核心是链接节点,每个节点同时存在于两个链表中:
-- Link 结构
{
dep = ReactiveObject, -- 依赖对象(被依赖的对象)
sub = ReactiveObject, -- 订阅者对象(依赖其他对象的对象)
-- 订阅者链表指针(垂直方向)
prevSub = Link, -- 同一依赖的上一个订阅者
nextSub = Link, -- 同一依赖的下一个订阅者
-- 依赖链表指针(水平方向)
prevDep = Link, -- 同一订阅者的上一个依赖
nextDep = Link -- 同一订阅者的下一个依赖
}使用位运算管理对象状态,提高性能:
local ReactiveFlags = {
None = 0, -- 0000000: 默认状态
Mutable = 1, -- 0000001: 可变对象(Signal/Computed)
Watching = 2, -- 0000010: 监听状态(Effect)
RecursedCheck = 4, -- 0000100: 循环依赖检查中
Recursed = 8, -- 0001000: 已被访问(递归标记)
Dirty = 16, -- 0010000: 脏值,需要更新
Pending = 32, -- 0100000: 可能脏值,需要检查
}
local EffectFlags = {
Queued = 64, -- 1000000: 已加入执行队列
}依赖追踪的核心是隐式依赖收集机制:
-- 全局状态追踪
local g_activeSub = nil -- 当前活跃的订阅者
local g_activeScope = nil -- 当前活跃的作用域
-- 依赖收集流程
function collectDependency(dep)
if g_activeSub then
reactive.link(dep, g_activeSub)
elseif g_activeScope then
reactive.link(dep, g_activeScope)
end
endfunction reactive.link(dep, sub)
-- 1. 重复检查:避免重复链接
local prevDep = sub.depsTail
if prevDep and prevDep.dep == dep then
return
end
-- 2. 循环依赖处理
local recursedCheck = bit.band(sub.flags, ReactiveFlags.RecursedCheck)
if recursedCheck > 0 then
-- 在递归检查中,特殊处理
handleRecursiveLink(dep, sub, prevDep)
return
end
-- 3. 订阅者重复检查
local prevSub = dep.subsTail
if prevSub and prevSub.sub == sub then
return
end
-- 4. 创建新链接
local newLink = createLink(dep, sub, prevDep, nil, prevSub)
-- 5. 更新链表指针
updateLinkPointers(newLink, dep, sub)
endfunction reactive.endTracking(sub)
-- 找到需要清理的依赖起点
local depsTail = sub.depsTail
local toRemove = sub.deps
if depsTail then
toRemove = depsTail.nextDep -- 从最后访问的依赖之后开始清理
end
-- 清理所有未被重新访问的依赖
while toRemove do
toRemove = reactive.unlink(toRemove, sub)
end
-- 清理递归检查标记
sub.flags = bit.band(sub.flags, bit.bnot(ReactiveFlags.RecursedCheck))
end当 Signal 值发生变化时,需要将"脏值"状态传播给所有依赖它的对象:
function reactive.propagate(link)
local stack = nil -- 用于处理分支的栈
while link do
local sub = link.sub
local flags = sub.flags
-- 检查对象类型和状态
if bit.band(flags, ReactiveFlags.Mutable | ReactiveFlags.Watching) > 0 then
-- 根据当前状态决定新状态
local newFlags = calculateNewFlags(flags)
sub.flags = newFlags
-- 如果是监听对象(Effect),加入执行队列
if bit.band(newFlags, ReactiveFlags.Watching) > 0 then
reactive.notify(sub)
end
-- 如果是可变对象(Computed),继续传播
if bit.band(newFlags, ReactiveFlags.Mutable) > 0 then
local subSubs = sub.subs
if subSubs then
-- 处理分支:保存当前状态到栈
if subSubs.nextSub then
stack = {value = link.nextSub, prev = stack}
end
link = subSubs
continue
end
end
end
-- 移动到下一个订阅者
link = link.nextSub
-- 如果当前分支结束,从栈中恢复
if not link and stack then
link = stack.value
stack = stack.prev
end
end
end在访问 Computed 值时,需要检查其依赖是否发生了变化:
function reactive.checkDirty(link, sub)
local checkDepth = 0
local stack = nil
while link do
local dep = link.dep
local depFlags = dep.flags
-- 检查依赖状态
if bit.band(depFlags, ReactiveFlags.Dirty) > 0 then
return true -- 依赖确实脏了
elseif bit.band(depFlags, ReactiveFlags.Mutable | ReactiveFlags.Dirty) ==
(ReactiveFlags.Mutable | ReactiveFlags.Dirty) then
-- 依赖是可变且脏的,需要更新
if reactive.update(dep) then
reactive.shallowPropagate(dep.subs)
return true
end
elseif bit.band(depFlags, ReactiveFlags.Mutable | ReactiveFlags.Pending) ==
(ReactiveFlags.Mutable | ReactiveFlags.Pending) then
-- 依赖可能脏,需要递归检查
stack = {value = link, prev = stack}
link = dep.deps
sub = dep
checkDepth = checkDepth + 1
continue
end
link = link.nextDep
end
return false
end系统实现了多层次的自动内存管理:
- 依赖清理: 当对象不再被访问时,自动清理其依赖关系
- 订阅者清理: 当对象不再有订阅者时,触发清理回调
- 循环引用处理: 通过弱引用和及时清理避免循环引用
function reactive.unwatched(node)
if node.getter then
-- Computed 对象:清理依赖并标记为脏
local toRemove = node.deps
if toRemove then
node.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty
end
-- 清理所有依赖
while toRemove do
toRemove = reactive.unlink(toRemove, node)
end
elseif not node.previousValue then
-- Effect 对象:执行清理操作
reactive.effectOper(node)
end
end- 及时解除链接: 在对象销毁时立即解除所有依赖关系
- 栈溢出保护: 使用迭代而非递归算法处理深层依赖
- 循环依赖检测: 使用标记位检测和处理循环依赖
使用位运算进行状态检查和更新,比传统的布尔值操作快数倍:
-- 传统方式
if obj.isDirty or obj.isPending then
-- 处理逻辑
end
-- 位运算方式
if bit.band(obj.flags, ReactiveFlags.Dirty | ReactiveFlags.Pending) > 0 then
-- 处理逻辑
end双向链表提供 O(1) 的插入和删除操作:
-- O(1) 插入操作
function insertLink(newLink, prevLink, nextLink)
newLink.prev = prevLink
newLink.next = nextLink
if prevLink then prevLink.next = newLink end
if nextLink then nextLink.prev = newLink end
end
-- O(1) 删除操作
function removeLink(link)
if link.prev then link.prev.next = link.next end
if link.next then link.next.prev = link.prev end
end通过队列机制实现批量更新,减少重复计算:
-- 批量更新状态
local g_batchDepth = 0
local g_queuedEffects = {}
function reactive.startBatch()
g_batchDepth = g_batchDepth + 1
end
function reactive.endBatch()
g_batchDepth = g_batchDepth - 1
if g_batchDepth == 0 then
reactive.flush() -- 执行所有排队的副作用
end
endComputed 值只在被访问时才计算,并缓存结果:
function computedOper(this)
local flags = this.flags
-- 只有在脏值或可能脏值时才重新计算
if bit.band(flags, ReactiveFlags.Dirty) > 0 or
(bit.band(flags, ReactiveFlags.Pending) > 0 and
reactive.checkDirty(this.deps, this)) then
if reactive.updateComputed(this) then
-- 值发生变化,通知订阅者
local subs = this.subs
if subs then
reactive.shallowPropagate(subs)
end
end
end
return this.value -- 返回缓存的值
end| 操作 | 复杂度 | 说明 |
|---|---|---|
| Signal 读取 | O(1) | 直接返回值 + 依赖注册 |
| Signal 写入 | O(n) | n 为直接订阅者数量 |
| Computed 读取 | O(d) | d 为依赖深度 |
| Effect 创建 | O(1) | 创建对象和注册 |
| 依赖链接 | O(1) | 双向链表插入 |
| 依赖解除 | O(1) | 双向链表删除 |
| 脏值传播 | O(n) | n 为依赖图中受影响的节点数 |
| 结构 | 复杂度 | 说明 |
|---|---|---|
| Signal | O(1) | 固定大小的对象 |
| Computed | O(d) | d 为依赖数量 |
| Effect | O(d) | d 为依赖数量 |
| 依赖图 | O(V + E) | V 为节点数,E 为边数 |
- 读取密集型优化: Signal 和 Computed 的读取操作都是常数时间
- 写入批量优化: 通过批量更新减少写入操作的开销
- 内存效率: 使用双向链表减少内存碎片
- 缓存友好: 局部性良好的数据结构设计
| 特性 | Alien Signals | Vue.js |
|---|---|---|
| 依赖追踪 | 隐式,基于函数调用 | 显式,基于属性访问 |
| 数据结构 | 双向链表 | 数组 + WeakMap |
| 更新策略 | 推拉混合 | 推模式 |
| 内存管理 | 自动清理 | 垃圾回收依赖 |
| 性能 | 极高(位运算优化) | 高 |
| 特性 | Alien Signals | MobX |
|---|---|---|
| API 设计 | 函数式 | 面向对象 |
| 依赖收集 | 编译时 + 运行时 | 运行时 |
| 状态管理 | 位运算标记 | 对象属性 |
| 批量更新 | 内置支持 | 需要额外配置 |
| 学习曲线 | 平缓 | 较陡峭 |
| 特性 | Alien Signals | Solid.js |
|---|---|---|
| 编译优化 | 运行时优化 | 编译时优化 |
| 细粒度更新 | 支持 | 支持 |
| 内存占用 | 极低 | 低 |
| 跨平台性 | 优秀(Lua) | 良好(JS) |
| 生态系统 | 新兴 | 成熟 |
创新性地使用双向链表同时管理依赖关系和订阅关系,实现了:
- O(1) 的依赖添加和删除
- 高效的内存利用
- 简化的遍历算法
使用位运算管理对象状态,相比传统的布尔值方式:
- 内存占用减少 75%
- 状态检查速度提升 3-5 倍
- 支持复合状态的原子操作
结合推模式和拉模式的优点:
- 推模式:及时通知变化
- 拉模式:惰性计算,避免不必要的计算
- 智能调度:根据访问模式自动优化
根据更新频率自动调整批量策略:
- 高频更新:自动启用批量模式
- 低频更新:立即执行模式
- 混合场景:智能切换
这些技术创新使得 Alien Signals 在保持简洁 API 的同时,实现了极高的性能和内存效率。
本技术文档详细解析了 Alien Signals 的核心实现原理,为深入理解和优化响应式系统提供了理论基础。
让我们通过一个完整的购物车系统来深入理解 Alien Signals 的响应式机制。这个案例展示了多层依赖关系、批量更新和副作用的复杂交互。
local reactive = require("reactive")
local signal = reactive.signal
local computed = reactive.computed
local effect = reactive.effect
-- 1. 基础数据信号
local itemPrice = signal(100) -- 商品单价
local quantity = signal(2) -- 商品数量
local discountRate = signal(0.1) -- 折扣率
local taxRate = signal(0.08) -- 税率
-- 2. 第一层计算值
local subtotal = computed(function()
print("计算 subtotal")
return itemPrice() * quantity()
end)
local discountAmount = computed(function()
print("计算 discountAmount")
return subtotal() * discountRate()
end)
-- 3. 第二层计算值
local afterDiscount = computed(function()
print("计算 afterDiscount")
return subtotal() - discountAmount()
end)
local taxAmount = computed(function()
print("计算 taxAmount")
return afterDiscount() * taxRate()
end)
-- 4. 最终计算值
local finalTotal = computed(function()
print("计算 finalTotal")
return afterDiscount() + taxAmount()
end)
-- 5. 副作用:UI 更新
local uiUpdateCount = signal(0)
effect(function()
print("UI 更新 - 总价: " .. finalTotal())
uiUpdateCount(uiUpdateCount() + 1)
end)
-- 6. 副作用:日志记录
effect(function()
print("日志 - 小计: " .. subtotal() .. ", 折扣: " .. discountAmount())
end)
-- 测试更新
print("=== 初始化完成 ===")
print("=== 更新商品数量 ===")
quantity(3)
print("=== 批量更新价格和折扣 ===")
reactive.startBatch()
itemPrice(120)
discountRate(0.15)
reactive.endBatch()依赖图结构:
itemPrice(100) quantity(2)
\ /
\ /
\ /
subtotal(200)
/ \
/ \
discountRate(0.1) taxRate(0.08)
| |
discountAmount(20) |
| |
\ |
\ |
afterDiscount(180) |
\ |
\ |
\ |
taxAmount(14.4) |
\ |
\ |
\ |
finalTotal(194.4)
/ \
/ \
UI Effect Log Effect
步骤 1-4: 创建基础信号
itemPrice = Signal{value: 100, subs: null, flags: Mutable}
quantity = Signal{value: 2, subs: null, flags: Mutable}
discountRate = Signal{value: 0.1, subs: null, flags: Mutable}
taxRate = Signal{value: 0.08, subs: null, flags: Mutable}
步骤 5: 创建 subtotal computed
subtotal = Computed{
value: null,
getter: function,
deps: null,
subs: null,
flags: Mutable|Dirty
}
步骤 6: 创建 discountAmount computed
discountAmount = Computed{
value: null,
getter: function,
deps: null,
subs: null,
flags: Mutable|Dirty
}
... 其他 computed 类似 ...
步骤 9: 创建 UI Effect
uiEffect = Effect{
fn: function,
deps: null,
subs: null,
flags: Watching
}
步骤 10: 执行 UI Effect 函数
g_activeSub = uiEffect // 设置活跃订阅者
调用 finalTotal()
├─ g_activeSub 存在,建立依赖: finalTotal -> uiEffect
├─ finalTotal.flags = Mutable|Dirty,需要计算
├─ 调用 finalTotal.getter()
│ ├─ 调用 afterDiscount()
│ │ ├─ 建立依赖: afterDiscount -> finalTotal
│ │ ├─ afterDiscount.flags = Mutable|Dirty,需要计算
│ │ ├─ 调用 afterDiscount.getter()
│ │ │ ├─ 调用 subtotal()
│ │ │ │ ├─ 建立依赖: subtotal -> afterDiscount
│ │ │ │ ├─ subtotal.flags = Mutable|Dirty,需要计算
│ │ │ │ ├─ 调用 subtotal.getter()
│ │ │ │ │ ├─ 调用 itemPrice()
│ │ │ │ │ │ ├─ 建立依赖: itemPrice -> subtotal
│ │ │ │ │ │ └─ 返回 100
│ │ │ │ │ ├─ 调用 quantity()
│ │ │ │ │ │ ├─ 建立依赖: quantity -> subtotal
│ │ │ │ │ │ └─ 返回 2
│ │ │ │ │ └─ 返回 100 * 2 = 200
│ │ │ │ ├─ subtotal.value = 200
│ │ │ │ └─ 返回 200
│ │ │ ├─ 调用 discountAmount()
│ │ │ │ ├─ 建立依赖: discountAmount -> afterDiscount
│ │ │ │ ├─ discountAmount.flags = Mutable|Dirty,需要计算
│ │ │ │ ├─ 调用 discountAmount.getter()
│ │ │ │ │ ├─ 调用 subtotal()(已计算,直接返回)
│ │ │ │ │ │ ├─ 建立依赖: subtotal -> discountAmount
│ │ │ │ │ │ └─ 返回 200
│ │ │ │ │ ├─ 调用 discountRate()
│ │ │ │ │ │ ├─ 建立依赖: discountRate -> discountAmount
│ │ │ │ │ │ └─ 返回 0.1
│ │ │ │ │ └─ 返回 200 * 0.1 = 20
│ │ │ │ ├─ discountAmount.value = 20
│ │ │ │ └─ 返回 20
│ │ │ └─ 返回 200 - 20 = 180
│ │ ├─ afterDiscount.value = 180
│ │ └─ 返回 180
│ ├─ 调用 taxAmount()
│ │ ├─ 建立依赖: taxAmount -> finalTotal
│ │ ├─ taxAmount.flags = Mutable|Dirty,需要计算
│ │ ├─ 调用 taxAmount.getter()
│ │ │ ├─ 调用 afterDiscount()(已计算,直接返回)
│ │ │ │ ├─ 建立依赖: afterDiscount -> taxAmount
│ │ │ │ └─ 返回 180
│ │ │ ├─ 调用 taxRate()
│ │ │ │ ├─ 建立依赖: taxRate -> taxAmount
│ │ │ │ └─ 返回 0.08
│ │ │ └─ 返回 180 * 0.08 = 14.4
│ │ ├─ taxAmount.value = 14.4
│ │ └─ 返回 14.4
│ └─ 返回 180 + 14.4 = 194.4
├─ finalTotal.value = 194.4
└─ 返回 194.4
输出: "UI 更新 - 总价: 194.4"
g_activeSub = null // 恢复
步骤 1: 更新信号
quantity.value = 3
quantity.flags = Mutable|Dirty
步骤 2: 传播脏状态
调用 reactive.propagate(quantity.subs)
├─ link = quantity.subs (指向 subtotal)
├─ sub = subtotal
├─ processSubscriberFlags(subtotal, subtotal.flags, link)
│ ├─ subtotal.flags 包含 Mutable
│ ├─ 设置 subtotal.flags |= Pending
│ └─ 返回处理后的标志
├─ subtotal 是 Mutable,继续传播到 subtotal.subs
├─ 遍历 subtotal 的所有订阅者:
│ ├─ discountAmount: 设置 Pending 标志
│ └─ afterDiscount: 设置 Pending 标志
├─ 继续传播到 afterDiscount 的订阅者:
│ ├─ taxAmount: 设置 Pending 标志
│ └─ finalTotal: 设置 Pending 标志
└─ 最终传播到 finalTotal 的订阅者:
└─ uiEffect: 调用 reactive.notify(uiEffect)
步骤 3: 执行副作用
reactive.flush()
├─ 从队列取出 uiEffect
├─ uiEffect.flags 包含 Dirty|Pending
├─ 调用 reactive.checkDirty(uiEffect.deps, uiEffect)
│ ├─ 检查 finalTotal 依赖
│ ├─ finalTotal.flags 包含 Pending,需要检查
│ ├─ 递归检查 finalTotal 的依赖
│ │ ├─ afterDiscount: Pending,继续检查
│ │ │ ├─ subtotal: Pending,继续检查
│ │ │ │ ├─ itemPrice: 无变化
│ │ │ │ └─ quantity: Dirty! 返回 true
│ │ │ └─ 发现依赖确实脏了
│ │ └─ afterDiscount 需要更新
│ └─ 返回 true (确实需要更新)
├─ 重新执行 uiEffect.fn
│ ├─ 调用 finalTotal()
│ │ ├─ finalTotal 被标记为 Pending,需要检查依赖
│ │ ├─ 调用 reactive.checkDirty 确认需要更新
│ │ ├─ 重新计算 finalTotal
│ │ │ ├─ afterDiscount 需要重新计算
│ │ │ │ ├─ subtotal 需要重新计算
│ │ │ │ │ ├─ itemPrice(): 100 (无变化)
│ │ │ │ │ ├─ quantity(): 3 (已更新)
│ │ │ │ │ └─ 返回 100 * 3 = 300
│ │ │ │ ├─ discountAmount 需要重新计算
│ │ │ │ │ ├─ subtotal(): 300
│ │ │ │ │ ├─ discountRate(): 0.1
│ │ │ │ │ └─ 返回 300 * 0.1 = 30
│ │ │ │ └─ 返回 300 - 30 = 270
│ │ │ ├─ taxAmount 需要重新计算
│ │ │ │ ├─ afterDiscount(): 270
│ │ │ │ ├─ taxRate(): 0.08
│ │ │ │ └─ 返回 270 * 0.08 = 21.6
│ │ │ └─ 返回 270 + 21.6 = 291.6
│ │ └─ 返回 291.6
│ └─ 输出: "UI 更新 - 总价: 291.6"
└─ logEffect 类似执行...
输出: "计算 subtotal"
输出: "计算 discountAmount"
输出: "计算 afterDiscount"
输出: "计算 taxAmount"
输出: "计算 finalTotal"
输出: "UI 更新 - 总价: 291.6"
输出: "日志 - 小计: 300, 折扣: 30"
步骤 1: 开始批量
reactive.startBatch()
├─ g_batchDepth = 1
└─ 副作用不会立即执行
步骤 2: 更新 itemPrice(120)
itemPrice.value = 120
itemPrice.flags = Mutable|Dirty
调用 reactive.propagate(itemPrice.subs)
├─ 传播到 subtotal 及其下游
├─ 所有相关的 computed 被标记为 Pending
├─ uiEffect 被加入队列,但不执行(因为 g_batchDepth > 0)
└─ logEffect 被加入队列,但不执行
步骤 3: 更新 discountRate(0.15)
discountRate.value = 0.15
discountRate.flags = Mutable|Dirty
调用 reactive.propagate(discountRate.subs)
├─ 传播到 discountAmount 及其下游
├─ 相关的 computed 被标记为 Pending
├─ Effects 已在队列中,不重复添加
└─ 仍不执行(批量模式)
步骤 4: 结束批量
reactive.endBatch()
├─ g_batchDepth = 0
├─ 调用 reactive.flush()
├─ 执行队列中的所有副作用
│ ├─ uiEffect 执行一次(不是两次!)
│ │ ├─ 检查所有依赖的脏状态
│ │ ├─ 重新计算整个依赖链
│ │ │ ├─ subtotal: 120 * 3 = 360
│ │ │ ├─ discountAmount: 360 * 0.15 = 54
│ │ │ ├─ afterDiscount: 360 - 54 = 306
│ │ │ ├─ taxAmount: 306 * 0.08 = 24.48
│ │ │ └─ finalTotal: 306 + 24.48 = 330.48
│ │ └─ 输出: "UI 更新 - 总价: 330.48"
│ └─ logEffect 类似执行
└─ 清空队列
输出: "计算 subtotal"
输出: "计算 discountAmount"
输出: "计算 afterDiscount"
输出: "计算 taxAmount"
输出: "计算 finalTotal"
输出: "UI 更新 - 总价: 330.48"
输出: "日志 - 小计: 360, 折扣: 54"
初始化后的内存状态:
itemPrice quantity
| |
v v
subtotal <-------------------> discountAmount
| |
v v
afterDiscount <--------------> taxAmount
| |
v v
finalTotal
|
v
[uiEffect, logEffect]
链接结构详细视图:
itemPrice.subs -> Link{dep: itemPrice, sub: subtotal, nextSub: null}
quantity.subs -> Link{dep: quantity, sub: subtotal, nextSub: null}
subtotal.subs -> Link{dep: subtotal, sub: discountAmount, nextSub: Link{dep: subtotal, sub: afterDiscount}}
...
更新传播路径:
quantity(3) -> subtotal -> [discountAmount, afterDiscount] -> [taxAmount, finalTotal] -> [uiEffect, logEffect]
-
计算次数统计:
- 初始化: 每个 computed 计算一次
- 单个更新: 只重新计算受影响的 computed
- 批量更新: 所有更改合并,每个 computed 最多计算一次
-
内存使用:
- 双向链表: O(E) 空间,E 为依赖边数
- 状态标记: O(1) 位运算,高效内存使用
- 自动清理: 无内存泄漏
-
时间复杂度:
- 信号更新: O(S),S 为直接和间接订阅者数量
- 依赖检查: O(D),D 为依赖深度
- 批量更新: O(N),N 为受影响的节点数
这个复杂用例展示了 Alien Signals 如何通过巧妙的算法设计,在保持高性能的同时,提供了优雅的响应式编程体验。