@type-dom/signals
v0.6.3
Published
以下是关于 [TypeDom Signals 仓库](https://github.com/type-dom/signals) 的详细说明和使用指南,基于知识库内容整理:
Downloads
97
Readme
以下是关于 TypeDom Signals 仓库 的详细说明和使用指南,基于知识库内容整理:
一、TypeDom Signals 简介
1. 项目定位
- 核心目标:
- 提供一个高性能的 响应式信号库(Reactive Signal Library),用于 TypeDom 框架的状态管理和数据绑定。
- 基于 Push-Pull 模型 设计,优化内存占用和执行效率,适用于复杂业务场景。
- 适用场景:
- 需要高效状态更新的前端应用(如实时数据仪表盘、大型表单交互)。
- TypeDom 框架内部的状态管理模块(如组件间通信、全局状态共享)。
2. 核心特性
- 高性能设计:
- 遵循严格的性能约束(如不使用
Array/Set/Map、避免递归调用)。 - 参考 Alien Signals 的 Push-Pull 模型,优化依赖追踪和更新逻辑。
- 遵循严格的性能约束(如不使用
- 轻量灵活:
- 支持 TypeScript,提供类型定义(
.d.ts文件)。 - 模块化设计,可单独使用或与 TypeDom 框架无缝集成。
- 支持 TypeScript,提供类型定义(
- 开发者友好:
- 提供直观的 API(如
signal,computed,effect)。 - 支持作用域管理(
effectScope),方便清理副作用。
- 提供直观的 API(如
二、快速上手指南
1. 安装依赖
npm install @type-dom/signals
# 或使用 pnpm/yarn2. 基本用法示例
以下是一个简单的响应式状态管理示例:
步骤1:定义信号(Signal)
import { signal, computed, effect } from '@type-dom/signals';
// 创建一个基础信号
const count = signal(1);
// 创建计算属性
const doubleCount = computed(() => count.get() * 2);
// 创建副作用(自动响应数据变化)
effect(() => {
console.log(`Count is: ${count.get()}`);
});
// 输出: Count is: 1步骤2:更新信号
count.set(2);
// 输出: Count is: 2
console.log(doubleCount.get()); // 输出: 4步骤3:清理副作用
import { effectScope } from '@type-dom/signals';
const scope = effectScope();
scope.run(() => {
effect(() => {
console.log(`Scoped count: ${count.get()}`);
});
count.set(3); // 输出: Scoped count: 3
});
scope.stop(); // 停止所有副作用
count.set(4); // 不再输出三、核心功能与 API
1. 主要导出模块
| 模块名 | 功能描述 |
|--------|----------|
| signal | 创建可读写的响应式信号(如 count = signal(0))。 |
| computed | 创建基于其他信号的计算属性(自动缓存结果)。 |
| effect | 创建副作用函数,自动追踪依赖并重新执行。 |
| effectScope | 管理副作用的作用域,支持批量清理。 |
2. 关键配置与优化
- 依赖追踪:
- 通过
propagate和checkDirty函数实现非递归更新逻辑,减少调用栈开销。
- 通过
- 性能优化:
- 避免动态对象字段和复杂数据结构(如
Map/Set)。 - 类属性数量限制在 10 以内(参考 V8 快速属性优化)。
- 避免动态对象字段和复杂数据结构(如
- 内存管理:
- 使用
effectScope.stop()显式清理不再需要的副作用。
- 使用
四、与 TypeDom 框架集成
1. 在组件中使用信号
import { TypeDiv } from '@type-dom/framework';
import { signal, effect } from '@type-dom/signals';
class Counter extends TypeDiv {
setup() {
const count = signal(0);
this.addChildren(
new Button({
slot: 'Click me',
events: {
click: () => count.set(count.get() + 1),
}
}),
new Span({
slot: computed(() => 'Count is: ' + count.get())
})
)
}
}2. 全局状态管理
// store.ts
import { signal } from '@type-dom/signals';
export const globalState = signal({ theme: 'dark' });
// component.ts
import { globalState } from './store';
globalState.get().theme; // 获取当前主题
globalState.set({ theme: 'light' }); // 更新主题五、开发与调试建议
1. 本地开发
- 启动开发服务器:
npm run dev - 构建生产版本:
npm run build
2. 单元测试
- 使用 Jest 编写测试用例:
npm run test
3. 性能监控
- 通过
console.log或调试器检查信号更新频率。 - 使用 Chrome DevTools 的 Performance 面板分析关键路径。
六、常见问题与解答
Q1:如何避免信号更新时的无限循环?
- 解决方法:
- 确保副作用函数中不直接修改自身依赖的信号。
- 使用
effectScope管理副作用生命周期。
Q2:信号与 Vue 的 Reactive 有何区别?
- 对比:
- TypeDom Signals 基于 Push-Pull 模型,更新逻辑更高效(参考 Alien Signals 的 400% 性能提升)。
- Vue 的
reactive使用代理(Proxy),而 Signals 通过显式get/set管理依赖。
Q3:文档和社区支持
- 官方文档:访问 type-dom.github.io/signals 查看完整 API 和示例。
- 社区反馈:
- 提交 Issue 或在论坛讨论:GitHub Issues。
七、总结
TypeDom Signals 是一个 高性能、轻量级的响应式状态管理库,基于 Push-Pull 模型和严格的性能优化策略,适合需要高效更新逻辑的 TypeDom 项目。通过 signal, computed, effect 等 API,开发者可以轻松实现复杂的状态绑定和副作用管理。建议从基础用法(如计数器)开始实践,逐步探索作用域管理和全局状态共享等高级功能。
Usage
Basic APIs
import { signal, computed, effect } from '@type-dom/signals';
const count = signal(1);
const doubleCount = computed(() => count.get() * 2);
effect(() => {
console.log(`Count is: ${count.get()}`);
}); // Console: Count is: 1
console.log(doubleCount.get()); // 2
count.set(2); // Console: Count is: 2
console.log(doubleCount.get()); // 4Effect Scope
import { signal, effect, effectScope } from '@type-dom/signals';
const count = signal(1);
const stopScope = effectScope(() => {
effect(() => {
console.log(`Count in scope: ${count.get()}`);
}); // Console: Count in scope: 1
});
count.set(2); // Console: Count in scope: 2
stopScope();
count.set(3); // No console outputCreating Your Own Surface API
You can reuse alien-signals’ core algorithm via createReactiveSystem() to build your own signal API. For implementation examples, see:
- Starter template (implements
.get()&.set()methods like the Signals proposal) - stackblitz/alien-signals/src/index.ts
- proposal-signals/signal-polyfill#44
About propagate and checkDirty functions
In order to eliminate recursive calls and improve performance, we record the last link node of the previous loop in propagate and checkDirty functions, and implement the rollback logic to return to this node.
This results in code that is difficult to understand, and you don't necessarily get the same performance improvements in other languages, so we record the original implementation without eliminating recursive calls here for reference.
propagate
function propagate(link: Link): void {
do {
const sub = link.sub;
let flags = sub.flags;
if (flags & (ReactiveFlags.Mutable | ReactiveFlags.Watching)) {
if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed | ReactiveFlags.Dirty | ReactiveFlags.Pending))) {
sub.flags = flags | ReactiveFlags.Pending;
} else if (!(flags & (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed))) {
flags = ReactiveFlags.None;
} else if (!(flags & ReactiveFlags.RecursedCheck)) {
sub.flags = (flags & ~ReactiveFlags.Recursed) | ReactiveFlags.Pending;
} else if (!(flags & (ReactiveFlags.Dirty | ReactiveFlags.Pending)) && isValidLink(link, sub)) {
sub.flags = flags | ReactiveFlags.Recursed | ReactiveFlags.Pending;
flags &= ReactiveFlags.Mutable;
} else {
flags = ReactiveFlags.None;
}
if (flags & ReactiveFlags.Watching) {
notify(sub);
}
if (flags & ReactiveFlags.Mutable) {
const subSubs = sub.subs;
if (subSubs !== undefined) {
propagate(subSubs);
}
}
}
link = link.nextSub!;
} while (link !== undefined);
}checkDirty
function checkDirty(link: Link, sub: ReactiveNode): boolean {
do {
const dep = link.dep;
const depFlags = dep.flags;
if (sub.flags & ReactiveFlags.Dirty) {
return true;
} else if ((depFlags & (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) === (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) {
if (update(dep)) {
const subs = dep.subs!;
if (subs.nextSub !== undefined) {
shallowPropagate(subs);
}
return true;
}
} else if ((depFlags & (ReactiveFlags.Mutable | ReactiveFlags.Pending)) === (ReactiveFlags.Mutable | ReactiveFlags.Pending)) {
if (checkDirty(dep.deps!, dep)) {
if (update(dep)) {
const subs = dep.subs!;
if (subs.nextSub !== undefined) {
shallowPropagate(subs);
}
return true;
}
} else {
dep.flags = depFlags & ~ReactiveFlags.Pending;
}
}
link = link.nextDep!;
} while (link !== undefined);
return false;
}@type-dom/signals 响应式框架性能深度分析报告
1. 框架概述
- 名称:
@type-dom/signals - 测试场景覆盖:
- 基础操作:信号创建(
createSignals)、计算创建(createComputations)、信号更新(updateSignals)。 - 传播性能:避免传播(
avoidablePropagation)、广泛传播(broadPropagation)、深度传播(deepPropagation)。 - 复杂场景:钻石结构(
diamond)、重复观察者(repeatedObservers)、不稳定更新(unstable)。 - 特定基准测试:
molBench、cellx1000、动态负载测试(如2-10x5 - lazy80%、6-100x15 - dyn50%)。
- 基础操作:信号创建(
- 核心优势:
- 极低的初始化成本(
createSignals接近 Svelte v5 的极限性能)。 - 单元格计算(
cellx1000)全场景最优。 - 中等复杂度场景下的稳定性(如
molBench、diamond)。
- 极低的初始化成本(
2. 关键性能指标对比
以下数据基于测试结果(单位:毫秒),对比其他主流框架:
| 测试项 | @type-dom/signals | 最优框架 | 最差框架 | 对比分析 |
|----------------------|-----------------------|--------------|--------------|------------------------------------------------------------------------------|
| createSignals | 2.20 | 2.00 (Svelte v5) | 93.00 (x-reactivity) | 接近 Svelte v5 的极限性能,远优于 Vue(9.50 ms)和 Angular(40.60 ms)。 |
| createComputations | 146.40 | 122.60 (Alien Signals) | 1344.50 (Angular Signals) | 优于大多数框架,仅略逊于 Alien Signals(122.60 ms)。 |
| updateSignals | 819.00 | 585.10 (Alien Signals) | 1963.70 (x-reactivity) | 中等水平,低于 Preact Signals(643.30 ms)和 Alien Signals(585.10 ms)。 |
| cellx1000 | 10.00 | 10.00 (自身) | 61.30 (s-js) | 全场景最优,远超其他框架(如 Svelte v5 19.90 ms、SolidJS 19.50 ms)。 |
| molBench | 616.20 | 605.50 (Alien Signals) | 1331.10 (Vue) | 接近 Alien Signals(605.50 ms),显著优于 Vue(1331.10 ms)。 |
| 2-10x5 - lazy80% | 512.40 | 408.90 (Alien Signals) | 1886.30 (x-reactivity) | 优于 Vue(619.50 ms)和 Svelte v5(1272.40 ms),接近 Alien Signals。 |
| 6-100x15 - dyn50% | 380.60 | 260.60 (Preact Signals) | 1066.10 (x-reactivity) | 优于 Vue(430.50 ms)和 Svelte v5(426.90 ms),接近 Preact Signals(260.60 ms)。 |
3. 优势与劣势分析
3.1 优势
- 极快的信号初始化
createSignals仅需 2.20 ms,接近 Svelte v5 的极限性能(2.00 ms),远优于 Vue(9.50 ms)和 Angular(40.60 ms)。- 适用场景:适合需要快速启动的轻量级应用或需要频繁创建信号的场景。
- 卓越的单元格计算能力
cellx1000全场景最优(10.00 ms),远超 Svelte v5(19.90 ms)、SolidJS(19.50 ms)和 Preact Signals(11.90 ms)。- 适用场景:适合处理密集型数据流、表格计算或网格化数据处理。
- 中等复杂度场景的稳定性
molBench(616.20 ms)和diamond(307.50 ms)表现均衡,优于 Vue(1331.10 ms 和 396.70 ms)。- 适用场景:适合中小型应用或需要平衡性能与功能的场景。
- 高效的传播控制
avoidablePropagation(168.00 ms)和deepPropagation(115.30 ms)表现优异,优于 Vue(439.80 ms 和 247.60 ms)。- 适用场景:适合需要精细控制依赖更新的场景(如复杂状态管理)。
3.2 劣势
- 更新性能的瓶颈
updateSignals(819.00 ms)中等,低于 Preact Signals(643.30 ms)和 Alien Signals(585.10 ms)。- 潜在问题:在高频更新场景(如实时数据流)中可能成为性能瓶颈。
- 动态负载测试的优化空间
2-10x5 - lazy80%(512.40 ms)和6-100x15 - dyn50%(380.60 ms)表现良好,但未达到 Alien Signals 或 Preact Signals 的顶尖水平。- 优化方向:需进一步优化算法或内存管理以适应极端动态负载。
- 复杂场景的优化潜力
molBench(616.20 ms)虽优于 Vue(1331.10 ms),但与 Alien Signals(605.50 ms)差距仅 10 ms。- 优化方向:需深入分析算法实现,减少微小性能损耗。
4. 与其他框架的对比
| 对比维度 | @type-dom/signals | Alien Signals | Preact Signals | Vue | SolidJS |
|----------------------|-----------------------|-------------------|--------------------|------------------|------------------|
| createSignals | 2.20 (接近最优) | 6.70 | 9.20 | 9.50 | 12.90 |
| cellx1000 | 10.00 (全场景最优) | 9.00 | 11.90 | 17.50 | 19.50 |
| updateSignals | 819.00 (中等) | 585.10 (最优) | 643.30 | 734.20 | 1536.70 (最差) |
| molBench | 616.20 (接近最优) | 605.50 (最优) | 613.70 | 1331.10 (最差) | 753.00 |
| 动态负载测试 | 512.40 (中等) | 408.90 (最优) | 440.90 | 619.50 | 2150.30 (最差) |
5. 适用场景推荐
| 场景类型 | 推荐程度 | 理由 |
|----------------------|--------------|--------------------------------------------------------------------------|
| 快速初始化 | ⭐⭐⭐⭐⭐ | createSignals 极快(2.20 ms),适合需要快速启动的轻量级应用。 |
| 单元格/网格计算 | ⭐⭐⭐⭐⭐ | cellx1000 全场景最优(10.00 ms),适合处理密集型数据流或表格计算。 |
| 中等复杂度应用 | ⭐⭐⭐⭐ | molBench(616.20 ms)和 diamond(307.50 ms)表现均衡,适合中小型项目。 |
| 高频更新场景 | ⭐⭐⭐ | updateSignals(819.00 ms)中等,需谨慎用于需要极高频率更新的场景。 |
6. 结论与建议
- 总结:
@type-dom/signals是一个 轻量级、高效的响应式框架,在 信号创建、单元格计算和中等复杂度场景 中表现突出,适合对初始化速度和数据流处理有较高要求的项目。 - 建议:
- 优先选择场景:快速启动、单元格计算、中等复杂度应用。
- 需优化方向:
- 提升
updateSignals性能以适应高频更新场景。 - 进一步优化
molBench和动态负载测试的算法效率。
- 潜在风险:在大型应用或极端高频更新场景中,需通过实际测试验证其稳定性。
附录:详细数据表
| 测试项 | @type-dom/signals 时间 (ms) | 全框架最优值 (ms) | 全框架最差值 (ms) |
|----------------------|-------------------------------|------------------------|------------------------|
| createSignals | 2.20 | 2.00 (Svelte v5) | 93.00 (x-reactivity) |
| updateSignals | 819.00 | 585.10 (Alien Signals) | 1963.70 (x-reactivity) |
| cellx1000 | 10.00 | 10.00 (自身) | 61.30 (s-js) |
| molBench | 616.20 | 605.50 (Alien Signals) | 1331.10 (Vue) |
| 2-10x5 - lazy80% | 512.40 | 408.90 (Alien Signals) | 1886.30 (x-reactivity) |
| 6-100x15 - dyn50% | 380.60 | 260.60 (Preact Signals) | 1066.10 (x-reactivity) |
