SolidJS — 细粒度响应式 UI 框架
是什么
SolidJS 是一个**用 React 风格 JSX 但没有”重新渲染”**的前端框架。日常类比:React 是数据变了把整张菜单重写一遍再贴回墙上;Solid 是数据变了只画掉那一行换一笔——其它行根本没动过。
更技术一点说:你写一个组件函数,它一辈子只跑一次。函数里读到的”数据”实际上是一个个信号(signal),信号变了,框架直接更新订阅它的那一小段 DOM——不重跑组件、不做 virtual DOM diff。
function Hello() { const [name, setName] = createSignal('Jason') return <h1>你好,{name()}</h1>}看起来和 React 几乎一样,但语义完全相反——Hello 只跑一次,name() 才是真正的订阅入口。
为什么重要
不理解 Solid,下面这些事都没法解释:
- 比 React 快 1.5-3 倍:JS Framework Benchmark 长期前三,背后是”细粒度反应式 + 无 virtual DOM diff”两个机制叠加
- JSX 看起来像 React 但底层完全不同:组件函数只跑一次这个事实改写了所有”重渲染心智”,是理解现代响应式框架的钥匙
- 启发了 React Compiler:React 团队 2024 年推出的 React Compiler 借鉴了 Solid 编译期细粒度追踪的思路
- SolidStart 是 Next.js 的有力替代:同样是 SSR + 路由 + 数据加载,但 runtime 体积小得多
核心要点
Solid 的心智模型可以拆成 三块:
-
信号(Signal):存”会变的值”的最小单位。
createSignal(0)返回一对 getter / setter——count()读,setCount(1)写。读的时候自动登记自己是订阅者。类比:广播电台和收音机——按下一个频道(调用 getter)就自动收到后续广播。 -
派生(Effect / Memo):
createEffect跑副作用(拉数据、写日志、操作 DOM 之外的事),createMemo算派生值(缓存计算结果)。它们都自动追踪依赖——你在里面读了哪些 signal,哪些变了就重跑。 -
编译时 JSX 优化:Solid 的编译器把
<h1>{name()}</h1>拆成”一次创建 DOM + 一个订阅函数绑到那个文本节点”。没有 virtual DOM,没有 diff,只有最小订阅单位的精准更新。
实践案例
案例 1:最小计数器
import { createSignal } from 'solid-js'
function Counter() { const [count, setCount] = createSignal(0) return <button onClick={() => setCount(count() + 1)}>{count()}</button>}逐行解释:
createSignal(0)返回[getter, setter]——count是函数,不是变量- JSX 里
{count()}调用 getter——这一调用就把”这个文本节点”订阅到count - 点按钮 →
setCount(...)→ Solid 通知所有订阅者 → 只更新那个文本节点 Counter函数本身只跑了一次,整个生命周期都不会再跑
案例 2:用 createEffect 追副作用
import { createSignal, createEffect } from 'solid-js'
function Logger() { const [name, setName] = createSignal('Jason') createEffect(() => { console.log('name 变成了', name()) }) return <input value={name()} onInput={e => setName(e.target.value)} />}createEffect 自动追踪 name()——每次输入框变化就打日志。没有依赖数组——你读了什么它就追什么,比 React 的 useEffect([deps]) 心智简单很多。
案例 3:嵌套 createMemo 派生
import { createSignal, createMemo } from 'solid-js'
function Cart() { const [items, setItems] = createSignal([{ price: 10 }, { price: 20 }]) const total = createMemo(() => items().reduce((s, i) => s + i.price, 0)) const taxed = createMemo(() => total() * 1.1) return <div>含税总价:{taxed()}</div>}total 依赖 items,taxed 依赖 total。items 变 → total 重算 → taxed 重算 → 只更新那个 <div> 的文本。整条链路是惰性 + 缓存的——没人读 taxed() 它就不算。
踩过的坑
-
组件函数只跑一次,不能用 React useState 思维:在组件函数体里写
if (count() > 5) ...永远只走第一次的分支,因为函数本身不会重跑。条件渲染要用 JSX 内联表达式或<Show when={...}>。 -
必须
count()调用才订阅,写count是函数引用:<h1>{count}</h1>把 getter 函数本身渲染成字符串,不会订阅。新人最常见的报错来源。 -
JSX 块外面解构 props 会丢响应式:
function Foo(props) { const { x } = props; return <p>{x}</p> }——x一旦解构出来就是普通值,丢了 props 的 getter 代理,再变也不会更新。要写props.x或用官方的splitProps/mergeProps。 -
与 React 库不兼容,生态小很多:React 表单、动画、状态管理库(
react-hook-form、framer-motion、zustand)都不能直接用——Solid 有自己的对应物(solid-forms、@motionone/solid),但数量级少一两个。选 Solid 等于选一个更小的生态。
适用 vs 不适用场景
适用:
- 性能敏感的复杂界面(dashboard / 实时数据 / 编辑器)—— 细粒度更新优势明显
- 嵌入式 / 小体积场景 —— Solid runtime 比 React 小一个数量级(约 7KB gzip)
- 想体验”现代响应式”心智的学习项目 —— 比 React 更直接、更接近 Vue 3 的 Composition API
- SSR + 路由的中型应用 —— SolidStart 提供完整的 vite 集成方案
不适用:
- 团队已经全员熟 React —— 切换成本高,且 Solid 招聘市场远小于 React
- 重度依赖现成 React 库(
shadcn-ui/react-spring等)—— 在 Solid 里要找替代或自己写 - 静态营销页 —— 用 Astro / 11ty 更合适,Solid 的反应式优势用不上
- 团队还不熟”信号”心智 —— 解构丢响应式这种坑会反复踩,比 React 更需要训练
历史小故事(可跳过)
- 2018 年:Ryan Carniato 在工作中维护一个老 Knockout.js 项目,受其细粒度反应式启发,业余时间开始造 Solid 原型
- 2019 年 1 月:Solid 0.9 发布,第一次进入 JS Framework Benchmark 视野,性能榜单前三
- 2021 年 6 月:Solid 1.0 正式发布,API 稳定,社区开始增长
- 2022 年 8 月:SolidStart 公布,对标 Next.js / Remix,把 Solid 从”组件库”变成”全栈框架”
- 2024 年:React Compiler 公开,被普遍认为吸收了 Solid / Svelte 5 这一派”编译期细粒度追踪”的思路
学到什么
- 响应式不一定要靠”重渲染”:React 的”数据变 → 函数重跑 → diff DOM”只是一种实现,Solid 证明了”数据变 → 直接通知订阅者 → 改 DOM”是可行且更快的另一条路
- 信号是过去 5 年最重要的前端概念:Vue 3 的
ref、Svelte 5 的$state、Angular 的signal()、Preact 的signal——本质都是 Solid 这套思路的近亲 - JSX 不等于 React:JSX 只是语法,背后的语义(每次重跑 vs 只跑一次)由编译器和 runtime 共同决定
- 小生态的代价是真实的:性能优势不能直接翻译成生产力,团队选型要权衡”框架性能 + 生态成熟度 + 招聘”三件事
延伸阅读
- 官方文档:solidjs.com(教程互动式,1 小时跑通核心 API)
- 作者讲设计思路:Ryan Carniato — Building SolidJS(90 分钟从零讲反应式系统)
- 性能对比:JS Framework Benchmark(每次发版都更新,Solid 长期前三)
- 信号思想综述:Ryan Carniato — A Hands-on Introduction to Fine-Grained Reactivity
- react —— 对比阅读价值最高,理解”重渲染 vs 细粒度”两条路线
- vue —— Vue 3 的 ref / reactive 和 Solid 信号思想接近,是另一种工业级实现
关联
- react —— 同样的 JSX 语法,相反的执行模型;理解 Solid 必先对比 React
- vue —— Vue 3 Composition API 与 Solid 信号心智高度相似
- vite —— SolidStart 默认构建工具,Solid 编译插件就是 Vite plugin
- mobx —— React 生态里的”信号派”实现,思路和 Solid 一致但寄生在重渲染框架上
- zustand —— React 状态管理,Solid 因有 signal 不需要这类库
- tanstack-query —— Solid 也能用(有
@tanstack/solid-query端口),是少数跨框架的好库
反向链接
- astro —— Astro — 内容站点优先的 Web 框架
- mobx —— MobX — 让 state 像电子表格一样自动重算
- qwik —— Qwik — Resumable UI 框架
- react —— React UI 组件库
- self-adjusting —— Self-Adjusting Computation — 输入小幅变化时只重算受影响的那部分
- svelte —— Svelte — 编译时 UI 框架
- tanstack-query —— TanStack Query — 数据获取与缓存库
- vite —— Vite — 浏览器自己加载源码的构建工具
- vue —— Vue.js — 渐进式 UI 框架
- zustand —— Zustand — 极简 React 状态管理