0de9b6e66e
- core/table-core: Headless 表格内核(排序/筛选/分页/选择插件 + 冒烟 9 项) - core/frame: signal/computed/effect + hyperscript 微框架(零依赖) - skin/ui: 16 个 DOM 组件(含自绘 select、滑动指示条 tabs、行选择 dataTable、form 表单校验) - design/tokens: --ua-* 设计令牌(light/dark) - tools/cli: ua 命令行(index/list/info/docs/style/mcp)+ MCP 注册表服务 - apps: demo 演示站(esbuild 单文件)+ docs 双轨文档(人类版 + llms-full.txt)
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
// 当前正在收集依赖的 effect;null 表示非追踪态
|
|
let active: (() => void) | null = null;
|
|
|
|
/*** 响应式信号:无参调用读值并收集依赖,传参写值并触发订阅 ***/
|
|
export interface Signal<T> {
|
|
(): T;
|
|
(value: T): void;
|
|
}
|
|
|
|
/*** 创建信号:框架状态的最小单元 ***/
|
|
export function signal<T>(initial: T): Signal<T> {
|
|
let value = initial;
|
|
const subs = new Set<() => void>();
|
|
const read = (...args: unknown[]) => {
|
|
if (args.length === 0) {
|
|
if (active) subs.add(active); // 依赖收集
|
|
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<T>;
|
|
}
|
|
|
|
/*** 副作用:立即执行一次并追踪内部读取的信号,依赖变化自动重跑 ***/
|
|
export function effect(fn: () => void): () => void {
|
|
const run = () => {
|
|
const prev = active;
|
|
active = run;
|
|
try {
|
|
fn();
|
|
} finally {
|
|
active = prev;
|
|
}
|
|
};
|
|
run();
|
|
return () => {
|
|
/* 演示级实现:不做依赖清理 */
|
|
};
|
|
}
|
|
|
|
/*** 计算值:由信号派生的只读视图,同样参与上层依赖收集 ***/
|
|
export function computed<T>(fn: () => T): () => T {
|
|
const out = signal<T>(undefined as T);
|
|
effect(() => out(fn()));
|
|
return () => out();
|
|
}
|