// 当前正在收集依赖的 effect;null 表示非追踪态 let active: (() => void) | null = null; // 当前 effect 已订阅到的「信号订阅集」集合:重跑前据此清旧订阅,注销时据此摘干净 let activeDeps: Set void>> | null = null; /*** 响应式信号:无参调用读值并收集依赖,传参写值并触发订阅 ***/ export interface Signal { (): T; (value: T): void; } /*** 创建信号:框架状态的最小单元 ***/ export function signal(initial: T): Signal { let value = initial; const subs = new Set<() => void>(); const read = (...args: unknown[]) => { if (args.length === 0) { if (active) { subs.add(active); // 依赖收集 activeDeps?.add(subs); // 同时记下「我订阅了哪个集」,注销时才能摘干净 } return value; } const v = args[0] as T; if (Object.is(v, value)) return; // 值未变不触发 value = v; for (const fn of [...subs]) fn(); // 拷贝后触发,防遍历中修改 }; return read as Signal; } /*** 副作用:立即执行一次并追踪内部读取的信号,依赖变化自动重跑;返回注销函数 ***/ export function effect(fn: () => void): () => void { const deps = new Set void>>(); // 本 effect 订阅到的所有信号订阅集 let stopped = false; // 注销标志:注销后即使还在某个订阅集里也不再执行 const run = () => { if (stopped) return; // 卫语句:注销后不再跑 for (const subs of deps) subs.delete(run); // 清上一轮订阅:条件分支换掉后的旧依赖不该再唤它 deps.clear(); const prev = active; const prevDeps = activeDeps; active = run; activeDeps = deps; try { fn(); } finally { active = prev; activeDeps = prevDeps; } }; run(); return () => { stopped = true; for (const subs of deps) subs.delete(run); // 真摘订阅:否则卸载后的组件仍会被信号唤醒 deps.clear(); }; } /*** 计算值:由信号派生的只读视图,同样参与上层依赖收集 ***/ export function computed(fn: () => T): () => T { const out = signal(undefined as T); effect(() => out(fn())); return () => out(); }