能否基于Observables实现Signals?原生实现与标准化提案探讨
Signals与Observables:相互实现、原生化探讨
1. 二者能否相互实现?
可以,但核心特性的映射会有明显取舍:
- Signals是值优先的,自带当前状态,订阅者会自动获取当前值并监听后续变化;Observables是流优先的,本质是事件序列,没有内置的「当前值」概念。
- 用Signal实现Observable:把每次Signal的状态变化转换成Observable的
next事件,初始值可作为第一个推送的事件。 - 用Observable实现Signal:需要手动维护一个当前值,订阅时先推送当前值,之后再转发Observable的事件到Signal订阅者。但Observable本身没有自动依赖追踪能力,这部分需要额外逻辑补充。
2. 能否用TC39 Observable提案实现Solid.js的createSignal?
基础的「值更新通知」版本可以,但会缺失Solid的核心能力:
Solid的createSignal不仅是值的订阅通知,更关键的是自动依赖追踪——在get()被调用时自动收集当前上下文的依赖,set()时精准触发依赖更新。
用TC39 Observable实现的基础版示例:
function createSignal(initialValue) { let currentValue = initialValue; const observerSet = new Set(); // 基于Observable构建订阅机制 const obs = new Observable(observer => { // 订阅时先推送当前值 observer.next(currentValue); observerSet.add(observer); return () => observerSet.delete(observer); }); const set = (newVal) => { currentValue = newVal; // 通知所有订阅者 observerSet.forEach(obs => obs.next(currentValue)); }; const get = () => { // 无法实现Solid的自动依赖追踪——Observable没有追踪get调用上下文的能力 return currentValue; }; return [get, set]; }
这个版本只能做到值的更新通知,但没法像Solid那样自动感知哪些组件/函数依赖了Signal,也就实现不了精准的响应式触发。
3. 原生实现Signal是否会更高效?
绝对会。目前各框架的Signal都是在JS层面基于闭包、WeakMap、Set等数据结构实现依赖收集和更新,而原生实现可以:
- 直接在引擎层面优化依赖追踪逻辑,避免JS层面的闭包和对象开销;
- 支持批量更新的原生级优化,减少不必要的视图重绘;
- 和浏览器渲染引擎深度联动,比如Signal变化时直接触发DOM更新,跳过框架的中间处理层;
- 利用引擎的垃圾回收机制更高效地清理无用依赖,降低内存泄漏风险。
4. 是否有必要推动TC39提案实现原生Signal?
有一定必要性,但也需要权衡利弊:
支持的理由
- 现在各框架的Signal实现(Solid、Preact Signals、Vue Refs等)API和行为存在差异,原生标准化后可以跨框架复用代码,降低开发者的学习成本;
- 框架作者可以不用重复实现底层的Signal逻辑,专注于上层的框架特性;
- 原生Signal可以和已有的Observable标准形成互补——Observable适合处理事件流,Signal适合处理状态值,覆盖不同的响应式场景。
需要考虑的问题
- Signal的核心特性(自动依赖追踪)和框架的渲染模型绑定很深,标准化可能需要做妥协,丢失部分框架特有的优化;
- 要避免和现有Observable标准的概念重叠,需要明确二者的边界和适用场景;
- 标准化过程需要协调各框架团队的需求,推进速度可能较慢。
内容的提问来源于stack exchange,提问作者llllvvuu
相关产品推荐
相关产品推荐

