上个季度,我们有一个客户项目,让我们有了完美的借口去尝试一直想做的事情:用 SolidJS 和 React 构建同一个应用,然后进行全面对比。不是 todo 应用。不是计数器。而是一个真实的生产仪表盘,具有身份验证、实时数据更新、复杂表单,以及呈现数千个数据点的图表模块。

结果让我们感到惊讶。不是因为某个框架"赢了" -- 而是因为权衡之处远比 Twitter 上的讨论要微妙得多。有些东西是我们预料到的。有些东西完全出乎我们的意料。这就是我们真正学到的。

目录

We Built the Same App in SolidJS and React: Signals vs Hooks

我们构建的应用

该应用是一个用于电商客户的实时分析仪表盘。以下是它包含的内容:

  • 身份验证流程,包含 JWT 令牌和刷新逻辑
  • 包含 6 个小部件面板的仪表盘,每个面板从不同的 API 端点拉取数据
  • 实时订单提要,使用 WebSocket 连接
  • 交互式图表,呈现 5,000+ 个数据点(使用图表库)
  • 复杂过滤表单,具有依赖下拉列表和日期范围选择器
  • 管理员设置面板,具有嵌套的状态管理
  • 响应式布局,带有侧边栏导航

两个版本都连接到相同的后端 API。两个都使用 TypeScript。两个都使用 Vite 作为构建工具。我们尽可能保持第三方依赖相似 -- 相同的图表库(Chart.js)、相同的 HTTP 客户端(ky)、相同的 WebSocket 包装器。

React 版本使用带有 hooks 的 React 19。SolidJS 版本使用 Solid 1.9。我们没有使用任何元框架(没有 Next.js,没有 SolidStart),因为我们想在路由和 SSR 混淆对比的情况下隔离框架差异。

Signals vs Hooks:思维模式转变

这是最重要的部分。老实说,我们的团队花了约一周的时间才能停止在 SolidJS 中写 React 风格的代码。

React Hooks 如何工作

你已经知道这个,但为了进行对比,值得明确陈述。React 的模型是:当状态改变时,组件函数重新执行。整个函数。每个 useState、每个 useMemo、每个 useCallback -- 它们都是围绕整个函数重新运行这一事实进行的机制。

// React:当状态改变时,整个函数重新运行
function OrderFeed() {
  const [orders, setOrders] = useState([]);
  const [filter, setFilter] = useState('all');
  
  // 除非我们进行 memoize,否则每次渲染都会重新计算
  const filteredOrders = useMemo(() => 
    orders.filter(o => filter === 'all' || o.status === filter),
    [orders, filter]
  );

  // 此 ref 在概念上每次渲染都会被重新创建
  const handleNewOrder = useCallback((order) => {
    setOrders(prev => [order, ...prev]);
  }, []);

  useEffect(() => {
    const ws = connectWebSocket(handleNewOrder);
    return () => ws.close();
  }, [handleNewOrder]);

  return <OrderList orders={filteredOrders} />;
}

Solid Signals 如何工作

Solid 的模型从根本上是不同的。组件函数只运行一次。之后,只有读取信号的特定表达式在该信号改变时重新执行。没有虚拟 DOM 差异。没有协调。响应图准确追踪哪些 DOM 节点依赖于哪些信号。

// Solid:此函数运行一次
function OrderFeed() {
  const [orders, setOrders] = createSignal([]);
  const [filter, setFilter] = createSignal('all');
  
  // 这会自动被追踪 -- 不需要依赖数组
  const filteredOrders = createMemo(() => 
    orders().filter(o => filter() === 'all' || o.status === filter())
  );

  // 不需要 useCallback -- 此闭包是稳定的
  const handleNewOrder = (order) => {
    setOrders(prev => [order, ...prev]);
  };

  // onMount 而不是带空依赖的 useEffect
  onMount(() => {
    const ws = connectWebSocket(handleNewOrder);
    onCleanup(() => ws.close());
  });

  return <OrderList orders={filteredOrders()} />;
}

这在实践中意味着什么

Solid 版本没有依赖数组。没有 useCallback。没有用于派生状态的 useMemo(尽管 createMemo 存在于昂贵计算中 -- 区别在于它是性能优化的可选项,而不是正确性的必需项)。

这是开发过程中真正困扰我们的地方:

  1. 在 Solid 中解构 props 会破坏响应性。 我们的一位初级开发人员在 Solid 组件中解构了 props,我们花了 45 分钟来调试为什么更新没有传播。在 React 中,解构是可以的,因为整个函数重新运行。在 Solid 中,你需要在 JSX 或追踪的作用域内访问 props.value

  2. Solid 中的早期返回很棘手。 你不能像在 React 中那样在 Solid 组件顶部执行 if (!data()) return <Loading />。组件函数只运行一次,所以该条件会永远不会重新评估。你需要使用 <Show when={data()}> 代替。

  3. 没有陈旧闭包 bug。 这是大赢家。在我们的 React 版本中,第一周我们有三个独立的陈旧闭包 bug,与 WebSocket 处理程序捕获旧状态有关。Solid 版本有零个,因为信号总是在访问时读取,而不是在闭包中捕获。

包大小对比

我们使用完全相同的 Vite 配置、gzip 压缩和代码分割策略测量了两个生产构建。

指标 React 19 SolidJS 1.9 差异
框架运行时 44.2 KB (gzip) 7.1 KB (gzip) -84%
总初始包 127.8 KB 89.3 KB -30%
总应用(所有块) 312.4 KB 274.1 KB -12%
首个块(关键路径) 68.4 KB 41.2 KB -40%
块数量 14 14 相同

Solid 的运行时明显更小。这不是新闻 -- Ryan Carniato 已经广泛讨论过这个问题。但让我们感到惊讶的是,一旦你添加真实的应用代码、第三方库和资产,总应用大小的差异显著缩小。

框架运行时对初始加载最重要。在 7.1 KB gzip 的情况下,Solid 基本上消失在噪声中。React 的 44 KB 是可以注意到的,特别是在移动连接上。

Tree Shaking

Solid 的 tree shaking 比 React 更好。未使用的 Solid 原始函数会被完全消除。使用 React 时,协调器和纤维架构无论你是否使用它们的所有功能都会被发送。我们通过在两者中构建最小页面来确认这一点 -- Solid 的下限更低。

We Built the Same App in SolidJS and React: Signals vs Hooks - architecture

渲染性能基准

我们使用 Chrome DevTools 性能配置文件、Lighthouse 和自定义计时工具运行了结构化基准。所有测试都在 M2 MacBook Pro 上进行,CPU 限制设置为 4 倍减速以模拟中端设备。

图表渲染(5,000 个数据点)

指标 React 19 SolidJS 1.9
初始渲染 142ms 89ms
在过滤器更改时重新渲染 67ms 23ms
渲染期间内存 18.4 MB 11.2 MB
创建的 DOM 节点 5,847 5,812

Solid 在重新渲染时始终更快,因为它不差异虚拟 DOM 树。当过滤器信号改变时,只有读取该信号的表达式会更新。React 19 的编译器改进有所帮助 -- 相同的测试在 React 18 上是 95ms 用于重新渲染 -- 但 Solid 仍然具有明显优势。

实时订单提要(每秒 100 次更新)

这是事情变得非常有趣的地方。我们将每秒 100 条 WebSocket 消息推入订单提要。

指标 React 19 SolidJS 1.9
帧丢失(每 10 秒) 12 2
平均绘制时间 8.3ms 3.1ms
最大绘制时间 34ms 11ms
CPU 使用率(平均) 24% 9%

Solid 在这个基准中绝对压倒性赢了。高频更新是细粒度响应性发挥最大优势的地方。React 正在批处理更新(这很好),但在每个批次中仍在差异比必要的更多 DOM。

我们应该注意:React 19 的 useTransition 和自动批处理使这比 React 18 好得多。而且对于大多数真实世界的应用,你不会推送每秒 100 次更新。在每秒 10 次更新时,两个框架都很流畅。

包含 40 个字段的复杂表单

指标 React 19 SolidJS 1.9
按键输入延迟 2-4ms <1ms
表单提交渲染 28ms 12ms
每次按键的组件重新渲染 3-8 1

在 React 中,在一个字段中输入会导致父组件重新渲染,除非我们仔细 memoize 所有内容。在 Solid 中,在字段中输入字面上只更新该字段的 DOM 文本节点。没有其他东西重新执行。

开发者体验和生态系统

性能不是一切。你必须实际构建这个东西,调试它,为它招聘,并维护它。

生态系统规模(截至 2026 年初)

因素 React SolidJS
npm 周下载量 ~28M ~85K
GitHub stars 233K+ 33K+
Stack Overflow 问题 470K+ ~2K
UI 组件库 50+ 成熟选项 5-8 个选项
职位发布(LinkedIn 美国) ~45,000 ~200
元框架 Next.js(成熟) SolidStart(测试版)

这是房间里的大象。React 的生态系统要大得多。当我们需要为 Solid 提供日期范围选择器时,我们必须移植 React 的或自己编写。在 React 中,我们有 15 个选项可以选择。

我们为 Solid 使用了 Kobalte 作为可访问的 UI 原始函数,它确实不错。但在组件覆盖范围方面,它还是无法与 Radix UI 相比。

调试

React DevTools 成熟、维护良好,是每个前端开发者都知道的。Solid 有自己的 DevTools 扩展,它还不错 -- 你可以检查信号图,这实际上对于追踪响应性 bug 比 React 的组件树视图更有用。但它不够精致。

TypeScript 支持

两者都很优秀。Solid 的 TypeScript 支持对 JSX 实际上稍好一些,因为编译器在构建时处理更多。我们在 Solid 代码库中进行了更少的类型体操。

生产中真正重要的权衡

在构建两个版本并将 Solid 版本部署到 staging 后(客户最终在生产中选择了 React -- 下面会详细说明),以下是真正重要的权衡:

招聘

我们的客户有一个由 8 名前端开发者组成的团队。所有人都了解 React。没有人使用过 Solid。培训时间估计:熟练度 2-3 周,掌握 2-3 个月。这是真实成本。这是大多数生产决策中的单一最大因素,也是为什么我们通常建议 React 或 Next.js 等框架用于需要频繁招聘的团队。

第三方库兼容性

我们必须为三个具有 React 特定集成的库编写自定义包装器。这增加了大约 20 小时的开发时间。在 React 项目中,这些小时不存在。

服务器端渲染

SolidStart 很有前景,但在我们的项目期间仍处于测试阶段。Next.js 经过战斗测试。对于需要生产级 SSR 和所有功能的项目,我们仍在根据内容模型选择 Next.js 开发Astro

性能在它重要的地方

老实说,对于这个特定的仪表盘,两个框架的生产性能都足够好。Solid 版本速度明显更快,但 React 版本从不。用户在大多数交互中不会注意到差异。

例外是高更新频率的实时提要。如果你的应用有这样的用例,Solid 的性能优势是有意义的,而不仅仅是基准吹嘘。

何时选择 SolidJS 而不是 React

在这个实验之后,这是我们对决定的诚实框架:

选择 SolidJS 当:

  • 你的应用具有高频响应式更新(仪表盘、交易平台、实时协作)
  • 包大小很关键(嵌入式小部件、微前端、移动优先)
  • 你的团队很小且愿意投资学习新的范式
  • 你想要细粒度响应性而不与框架对抗
  • 你正在构建新的东西并且不需要庞大的组件库生态系统

选择 React 当:

  • 你的团队已经了解 React(这一点通常是决定性的)
  • 你需要成熟的元框架(Next.js、Remix)
  • 第三方库可用性很重要
  • 你正在招聘并需要大量的人才库
  • 你的应用的性能要求是典型的(不是极端的)

对于我们的 headless CMS 开发项目,React 仍然占主导地位,因为 CMS 集成(Sanity、Contentful、Storyblok)具有一流的 React SDK。Solid 支持存在但通常由社区维护。

代码对比:真实模式

让我们看看项目中并排的一些真实模式。

带加载状态的数据获取

React:

function Dashboard() {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchDashboardData()
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <Skeleton />;
  if (error) return <ErrorBanner error={error} />;
  return <DashboardGrid data={data} />;
}

SolidJS:

function Dashboard() {
  const [data] = createResource(fetchDashboardData);

  return (
    <Suspense fallback={<Skeleton />}>
      <ErrorBoundary fallback={(err) => <ErrorBanner error={err} />}>
        <DashboardGrid data={data()} />
      </ErrorBoundary>
    </Suspense>
  );
}

Solid 的 createResourceSuspense 真的很优雅。React 也有 Suspense(而且它在 React 19 中变得更好),但 Solid 的版本感觉更自然工作。更少样板代码,更少状态变量要管理。

条件渲染

React:

{user ? (
  <Profile user={user} />
) : (
  <LoginPrompt />
)}

SolidJS:

<Show when={user()} fallback={<LoginPrompt />}>
  {(u) => <Profile user={u()} />}
</Show>

Solid 的 <Show> 组件存在是因为当组件函数只运行一次时,三元运算符不会以相同的方式工作。习惯之后,回调模式给你一个缩小的、非空的引用,这对 TypeScript 很好。

列表渲染

React:

{orders.map(order => (
  <OrderRow key={order.id} order={order} />
))}

SolidJS:

<For each={orders()}>
  {(order) => <OrderRow order={order} />}
</For>

Solid 的 <For> 不需要键,因为它按引用追踪数组项。当项在数组中移动时,Solid 移动实际的 DOM 节点。React 会卸载并重新挂载。这就是为什么 Solid 的列表性能这么好。

常见问题

SolidJS 在 2026 年生产就绪吗? yes。Solid 1.x 已经稳定了两年多。Cloudflare 和三星等公司在生产中使用过它。核心库是成熟和经过充分测试的。它周围的生态系统(SolidStart、组件库)比 React 的小,但增长势头强劲。问题不是 Solid 是否就绪 -- 而是你的团队和项目要求是否与其生态系统规模相符。

我可以从 React 增量迁移到 SolidJS 吗? 不太容易。尽管 JSX 语法相似,运行时模型从根本上是不同的。你不能在 React 应用内嵌入 Solid 组件(反之亦然),除非有 iframe 或 web 组件边界。迁移基本上会是一次重写。如果你正在考虑,从一个新的隔离功能或微前端开始,而不是尝试转换现有的 React 代码。

SolidJS 支持服务器端渲染吗? 支持。SolidStart 提供 SSR、流和服务器函数。它是可用的并且改进迅速,但截至 2026 年初,它仍然不如 Next.js 或 Remix 成熟。对于 SSR 繁重的项目,我们仍然会建议评估 Next.jsAstro,取决于你的内容需求。

React 19 的编译器与 Solid 的方法相比如何? React 19 的编译器(原名 React Forget)自动 memoize 组件以减少不必要的重新渲染。这是一个重大改进。但它仍在 React 的模型内工作,即重新运行组件函数和差异虚拟 DOM。Solid 完全跳过这两个步骤。编译器使 React 更快,但它不改变基本架构。在我们的基准中,带编译器的 React 19 比 React 18 快约 30%,但在更新繁重的场景中仍然比 Solid 慢。

Preact Signals 作为中间地带怎么样? Preact Signals(以及 @preact/signals-react 适配器)为 React 生态系统带来类似信号的响应性。这是一个有趣的方法,但它与 React 的核心模型对抗,而不是与之配合工作。我们简要测试了它,找到了 Suspense 和并发功能的边界情况。如果你想要信号,Solid 给你完整的体验而不需要阻抗失配。

SolidJS 比 React 更难学吗? 如果你已经了解 React,Solid 的 API 会看起来很熟悉 -- 相似的 JSX、相似的模式。困难部分是遗忘 React 的心智模型。你会去找 useEffect 模式,但它们不存在。你会解构 props 并破坏响应性。你会尝试早期返回并想知道为什么它们不工作。对于有经验的 React 开发者,预计约 1-2 周才能在 Solid 中变得高效,还要再花 1-2 个月才能停止写 React 风格的 Solid。

哪个框架的 TypeScript 支持更好? 两者都有优秀的 TypeScript 支持。Solid 有一点优势,因为它的编译器可以在某些模式中提供更窄的类型(比如 <Show> 中的回调),并且你不需要复杂 React hooks 有时需要的相同体量的通用类型参数。但老实说,两者都很好。这不应该是决定性因素。

我应该为我的下一个项目使用 SolidJS 吗? 这取决于你的约束。如果你是一个小团队,构建性能敏感的东西,并且愿意投资学习曲线和解决更小的生态系统,Solid 是一个真正出色的选择。如果你需要招聘 React 开发者、使用已建立的组件库或需要用于生产的战斗测试元框架,React 是更安全的赌注。没有普遍答案 -- 只有针对你具体情况的正确答案。如果你想讨论你的项目决定,与我们联系,我们可以帮助你评估权衡。