iter(g217): 补 frame 内核冒烟并修复 effect 注销

This commit is contained in:
HIsheR
2026-10-26 19:58:40 +08:00
parent d7ad79963b
commit 34985fff43
4 changed files with 119 additions and 5 deletions
+18 -3
View File
@@ -1,5 +1,7 @@
// 当前正在收集依赖的 effect;null 表示非追踪态
let active: (() => void) | null = null;
// 当前 effect 已订阅到的「信号订阅集」集合:重跑前据此清旧订阅,注销时据此摘干净
let activeDeps: Set<Set<() => void>> | null = null;
/*** 响应式信号:无参调用读值并收集依赖,传参写值并触发订阅 ***/
export interface Signal<T> {
@@ -13,7 +15,10 @@ export function signal<T>(initial: T): Signal<T> {
const subs = new Set<() => void>();
const read = (...args: unknown[]) => {
if (args.length === 0) {
if (active) subs.add(active); // 依赖收集
if (active) {
subs.add(active); // 依赖收集
activeDeps?.add(subs); // 同时记下「我订阅了哪个集」,注销时才能摘干净
}
return value;
}
const v = args[0] as T;
@@ -24,20 +29,30 @@ export function signal<T>(initial: T): Signal<T> {
return read as Signal<T>;
}
/*** 副作用:立即执行一次并追踪内部读取的信号,依赖变化自动重跑 ***/
/*** 副作用:立即执行一次并追踪内部读取的信号,依赖变化自动重跑;返回注销函数 ***/
export function effect(fn: () => void): () => void {
const deps = new Set<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();
};
}