iter(g217): 补 frame 内核冒烟并修复 effect 注销
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/*** 作用:@ui-arch/frame 内核冒烟——信号 / 副作用 / 计算值的语义断言(纯 JS,无需 DOM;跑 npm run smoke 执行) ***/
|
||||
import { computed, effect, signal } from '../dist/signal.js';
|
||||
|
||||
let pass = 0;
|
||||
let fail = 0;
|
||||
|
||||
/*** 断言:失败也继续跑完,最后统一给退出码(否则第一个红就看不到后面) ***/
|
||||
const ok = (label, cond) => {
|
||||
if (cond) {
|
||||
pass += 1;
|
||||
console.log(`ok - ${label}`);
|
||||
return;
|
||||
}
|
||||
fail += 1;
|
||||
console.error(`NOT OK - ${label}`);
|
||||
};
|
||||
|
||||
// —— 1. 信号的读写与同值短路 ——
|
||||
const count = signal(0);
|
||||
let hits = 0;
|
||||
effect(() => {
|
||||
count();
|
||||
hits += 1;
|
||||
});
|
||||
ok('effect 创建时立即执行一次', hits === 1);
|
||||
count(1);
|
||||
ok('写值后重跑', hits === 2);
|
||||
count(1);
|
||||
ok('写入相同值不触发(Object.is 短路)', hits === 2);
|
||||
|
||||
// —— 2. 注销后不再重跑 ——
|
||||
const live = signal('a');
|
||||
let liveHits = 0;
|
||||
const dispose = effect(() => {
|
||||
live();
|
||||
liveHits += 1;
|
||||
});
|
||||
live('b');
|
||||
ok('注销前正常重跑', liveHits === 2);
|
||||
dispose();
|
||||
live('c');
|
||||
ok('注销后写值不再重跑(dispose 必须真的摘订阅)', liveHits === 2);
|
||||
|
||||
// —— 3. 条件分支重追踪:上一轮读过、这一轮不读的信号不该再唤它 ——
|
||||
const flag = signal(true);
|
||||
const left = signal(0);
|
||||
const right = signal(0);
|
||||
let branchHits = 0;
|
||||
effect(() => {
|
||||
branchHits += 1;
|
||||
if (flag()) left();
|
||||
else right();
|
||||
});
|
||||
const afterInit = branchHits;
|
||||
flag(false);
|
||||
ok('切分支本身触发一次重跑', branchHits === afterInit + 1);
|
||||
const afterSwitch = branchHits;
|
||||
left(9);
|
||||
ok('切走后旧的依赖不再唤它(重追踪要清旧订阅)', branchHits === afterSwitch);
|
||||
right(9);
|
||||
ok('切走后新依赖仍唤它', branchHits === afterSwitch + 1);
|
||||
|
||||
// —— 4. computed:派生、链式、同值短路 ——
|
||||
const price = signal(10);
|
||||
const qty = signal(2);
|
||||
const total = computed(() => price() * qty());
|
||||
ok('computed 首次读即得派生值', total() === 20);
|
||||
price(3);
|
||||
ok('源变化后 computed 立即跟着变', total() === 6);
|
||||
let derivedHits = 0;
|
||||
effect(() => {
|
||||
total();
|
||||
derivedHits += 1;
|
||||
});
|
||||
const derivedBase = derivedHits;
|
||||
qty(3);
|
||||
ok('computed 变更会唤上层 effect(链式依赖成立)', derivedHits === derivedBase + 1);
|
||||
ok('链式计算值正确', total() === 9);
|
||||
|
||||
// —— 5. 多订阅者:扇出与互不影响 ——
|
||||
const fan = signal(0);
|
||||
let a = 0;
|
||||
let b = 0;
|
||||
const disposeA = effect(() => {
|
||||
fan();
|
||||
a += 1;
|
||||
});
|
||||
effect(() => {
|
||||
fan();
|
||||
b += 1;
|
||||
});
|
||||
fan(1);
|
||||
ok('一次写值唤全部订阅者', a === 2 && b === 2);
|
||||
disposeA();
|
||||
fan(2);
|
||||
ok('注销一个订阅者不影响另一个', a === 2 && b === 3);
|
||||
|
||||
console.log(`\n${fail === 0 ? 'SMOKE OK' : 'SMOKE FAILED'}(${pass} 通过 / ${fail} 失败)`);
|
||||
process.exit(fail === 0 ? 0 : 1);
|
||||
Reference in New Issue
Block a user