2026年最值得关注的JavaScript新特性:Signals,响应式编程的下一个十年

6 阅读7分钟

ScreenShot_2026-03-22_105410_567.png

从框架之争到语言标准,响应式编程的下一个十年已经到来

在现代前端开发中,响应式编程早已成为构建用户界面的核心范式。无论是React的Hooks、Vue的组合式API,还是SolidJS的细粒度更新,它们的底层都离不开一个共同的原理——Signals

如今,TC39委员会正在讨论将Signals纳入JavaScript语言本身。尽管目前它还处于Stage 1的早期阶段,但我们已经看到越来越多的框架开始以Signals模式重构其响应式核心。本文将深入介绍Signals是什么、为什么重要,以及它如何改变我们编写JavaScript的方式。


一、Signals是什么?

Signals是一种响应式状态原语——它表示一个随时间变化的值,并能够自动通知所有依赖这个值的代码进行更新。

可以把它理解为一个带有“自动订阅”功能的特殊变量。当你读取它的值时,当前执行的上下文(比如组件渲染函数)会自动订阅这个Signal;当你修改它的值时,所有订阅者会自动重新执行。

核心API

目前提案中的Signals提供三个核心概念:

概念说明
Signal.State可变的信号,持有状态值
Signal.Computed派生信号,基于其他信号计算值,自动缓存
Signal.subtle.Watcher观察者,用于批量响应信号变化(框架层使用)

最简单的示例

// 创建一个状态信号,初始值为0
const count = new Signal.State(0);

// 创建一个计算信号,依赖于count
const doubled = new Signal.Computed(() => count.get() * 2);

// 创建一个effect,当依赖的信号变化时自动执行
const watcher = new Signal.subtle.Watcher(() => {
  console.log(`count = ${count.get()}, doubled = ${doubled.get()}`);
});
watcher.watch(count, doubled);

// 触发更新
count.set(1); // 控制台输出: count = 1, doubled = 2
count.set(2); // 输出: count = 2, doubled = 4

这段代码展示了Signals的核心能力:

  • 状态count 是可变的
  • 派生doubled 自动跟随 count 变化
  • 响应:任何依赖 countdoubled 的副作用都会自动重新执行

二、Signals如何解决现有痛点?

1. 跨框架的状态共享

这是Signals最诱人的特性之一。目前,React的状态、Vue的状态、Solid的状态互不兼容,无法直接共享。如果Signals成为语言标准,你写出的状态管理逻辑将可以在任何框架中使用

// store.js - 纯原生Signals,不依赖任何框架
const user = new Signal.State({ name: '张三', age: 28 });
const userInfo = new Signal.Computed(() => `${user.get().name}${user.get().age}岁`);

export { user, userInfo };
// React组件中使用
import { userInfo } from './store';

function UserCard() {
  // 通过适配器(未来框架可能内置)将Signal转换为React状态
  const info = useSignal(userInfo);
  return <div>{info}</div>;
}
<!-- Vue组件中使用 -->
<template>
  <div>{{ info }}</div>
</template>

<script setup>
import { userInfo } from './store';
// 未来Vue可直接支持原生Signal
const info = userInfo; // 自动解包响应式
</script>

2. 细粒度更新与性能

React Hooks的重新渲染是以组件为单位的,即使只有一小部分状态变化,整个组件函数都会重新执行。而Signals天然支持细粒度更新

// 使用Signals的组件
function Counter() {
  const count = new Signal.State(0);
  
  // 只有这部分依赖于count的代码会在count变化时重新执行
  const display = () => <span>{count.get()}</span>;
  
  // 按钮点击事件不会导致整个组件重新渲染
  const handleClick = () => count.set(count.get() + 1);
  
  return (
    <div>
      {display()}  {/* 仅此处更新 */}
      <button onClick={handleClick}>+</button>
    </div>
  );
}

在SolidJS等基于Signals的框架中,这种细粒度更新是默认行为,带来了极致的性能表现。

3. 消除依赖数组

React的useEffectuseMemouseCallback需要手动指定依赖数组,稍有不慎就会导致bug。Signals通过自动依赖追踪彻底解决了这个问题:

// 无需手动指定依赖
effect(() => {
  console.log(`Count changed to: ${count.get()}`);
}); // 自动追踪count

// 对比React
useEffect(() => {
  console.log(`Count changed to: ${count}`);
}, [count]); // 必须手动维护依赖

三、Signals的内部工作原理

依赖追踪

Signals通过一个全局的“当前执行上下文”来实现自动依赖追踪。当一个Computed或effect运行时,它会:

  1. 设置一个全局标志,表示“我正在计算”
  2. 执行用户提供的函数
  3. 在函数执行过程中,每次调用signal.get()时,都会记录当前正在执行的上下文与这个Signal的依赖关系
  4. 执行结束后,依赖图构建完成

更新传播

当一个Signal的值改变时:

  • 所有直接依赖它的Computed被标记为“脏”
  • 这些Computed会在下一次被读取时重新计算
  • effect会异步批量执行,避免重复计算

避免Glitches

Signals采用Push-Pull混合模型:值的变化通过Push(推送)触发依赖标记,但实际计算通过Pull(拉取)在需要时进行。这种模型能有效避免“菱形依赖”导致的中间状态不一致问题。


四、Signals vs 其他响应式方案

特性原生SignalsReact HooksVue ref/computedSolidJS Signals
跨框架复用✅ 原生支持❌ 无法跨框架❌ 无法跨框架❌ 绑定框架
依赖追踪自动手动依赖数组编译时自动自动
细粒度更新❌ 组件级
学习成本
性能极高中等极高

五、为什么现在关注Signals?

1. 框架正在先行采用Signals模式

虽然原生Signals还在Stage 1,但Signals模式已经在多个主流框架中落地:

框架实现方式现状
SolidJS原生Signals全框架基于Signals,细粒度响应式标杆
AngularAngular Signalsv16引入,v17成为推荐响应式方案
PreactPreact Signals独立库,可脱离Preact使用
QwikQwik Signals细粒度响应式,支持惰性加载
Vueref/computed设计理念与Signals高度相似
Sveltestores通过编译器实现类似能力

这些框架的实践证明了Signals模式的可行性和优越性。一旦原生Signals落地,这些框架的底层完全可以替换为原生实现,从而获得更好的性能和跨框架互通性。

2. 社区工具链正在适配

许多状态管理库已经开始拥抱Signals模式:

  • MobX:虽然比Signals更强大,但其核心思想与Signals一致
  • Redux Toolkit:引入了createActioncreateReducer,但尚未完全转向Signals
  • Zustand:轻量级状态管理,可以很容易地基于Signals构建

3. TypeScript支持

TypeScript团队已经表示会全力支持Signals提案,提供完善的类型推断。目前已有实验性的类型定义可供使用。

六、现在可以做什么?

虽然原生Signals还不能用于生产环境,但我们可以:

1. 学习Signals思想

尝试使用现有框架中的Signals实现(如SolidJS、Preact Signals),理解响应式编程的最佳实践。

2. 关注提案进展

Star并关注TC39的proposal-signals仓库,参与讨论。

3. 使用polyfill

可以通过@preact/signals-coresolid-js提前体验Signals的开发体验。

npm install @preact/signals-core
import { signal, computed, effect } from '@preact/signals-core';

const count = signal(0);
const doubled = computed(() => count.value * 2);

effect(() => {
  console.log(`Count: ${count.value}, doubled: ${doubled.value}`);
});

count.value = 1; // 自动触发effect

结语

Signals不仅仅是另一个JavaScript特性,它代表了响应式编程从“框架专属”到“语言原生”的范式转变。虽然目前它还处于早期阶段,但众多现代框架的先行实践已经证明了Signals模式的巨大价值。

正如当年Promise统一了异步编程、Proxy统一了响应式元编程一样,Signals有潜力统一前端状态管理。无论你是框架作者、库开发者还是普通应用开发者,了解Signals都将帮助你更好地把握JavaScript的未来发展方向。

最后提醒:原生Signals目前仍处于Stage 1,API和语义都可能发生变化,切勿在生产环境中直接使用。但学习Signals思想,关注提案进展,将为你在下一个技术浪潮中抢占先机。


更多开发技巧、前沿资讯,欢迎关注我的微信公众号【编程智匠】。在这里,我会定期分享实战经验,帮你少走弯路,用技术创造价值。