我們用 SolidJS 和 React 建立了相同的應用程式:Signals 與 Hooks 的比較
上一季度,我們有一個客戶專案給了我們完美的藉口去做我們一直想嘗試的事情:用 SolidJS 和 React 建立相同的應用程式,然後比較一切。不是 todo 應用程式。不是計數器。一個真正的生產環境儀表板,包含身份驗證、實時數據更新、複雜表單和渲染數千個數據點的圖表模組。
結果令我們驚訝。不是因為某個框架「獲勝」——而是因為權衡遠比 Twitter 上的討論更為微妙。有些東西我們預期到了。有些東西完全出乎我們意料。這是我們實際學到的內容。
目錄
- 我們建立的應用程式
- Signals vs Hooks:心智模型的轉變
- Bundle Size 比較
- 渲染性能基準測試
- 開發者體驗和生態系統
- 真正重要的生產權衡
- 何時選擇 SolidJS 而非 React
- 程式碼比較:真實模式
- 常見問題

我們建立的應用程式
該應用程式是一個電子商務客戶的實時分析儀表板。以下是它包含的內容:
- 身份驗證流程,使用 JWT tokens 和刷新邏輯
- 包含 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');
// 除非我們記憶化,否則這個在每次渲染時重新計算
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 diff。沒有調和。反應圖追蹤哪些 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 存在於昂貴計算中——區別在於它是性能的選擇性加入,而不是正確性的必需)。
以下是開發期間實際上難倒我們的地方:
在 Solid 中解構 props 會破壞反應性。 我們有一位初級開發者在 Solid 組件中解構 props,我們花了 45 分鐘除錯為什麼更新沒有傳播。在 React 中,解構很好,因為整個函數重新執行。在 Solid 中,你需要在 JSX 或追蹤範圍內訪問
props.value。Solid 中的早期返回很棘手。 你不能在 Solid 組件的頂部執行
if (!data()) return <Loading />的方式,就像在 React 中那樣。組件函數只執行一次,所以該條件永遠不會重新評估。你需要<Show when={data()}>代替。沒有過期閉包 bugs。 這是大贏家。在我們的 React 版本中,我們在第一週內有三個與 WebSocket 處理程序捕獲舊狀態相關的過期閉包 bugs。Solid 版本沒有,因為信號總是在訪問時讀取,而不是在閉包中捕獲。
Bundle Size 比較
我們使用完全相同的 Vite 配置、gzip 壓縮和程式碼分割策略來測量兩個生產建構。
| 指標 | React 19 | SolidJS 1.9 | 差異 |
|---|---|---|---|
| 框架運行時 | 44.2 KB (gzip) | 7.1 KB (gzip) | -84% |
| 總初始 bundle | 127.8 KB | 89.3 KB | -30% |
| 總應用程式(所有 chunks) | 312.4 KB | 274.1 KB | -12% |
| 第一個 chunk(關鍵路徑) | 68.4 KB | 41.2 KB | -40% |
| Chunks 數量 | 14 | 14 | 相同 |
Solid 的運行時明顯更小。這不是新聞——Ryan Carniato 已經廣泛談論過這個。但令我們驚訝的是,一旦你添加真實應用程式碼、第三方庫和資源,總應用程式大小差異就會明顯縮小。
框架運行時對初始加載最重要。而在 7.1 KB gzipped,Solid 基本上消失在噪聲中。React 的 44 KB 是顯著的,特別是在移動連接上。
樹搖晃
Solid 比 React 樹搖晃更好。未使用的 Solid 原始類型會完全被消除。使用 React,協調器和 fiber 架構會被運送,無論你是否使用所有功能。我們通過在兩者中建立最小頁面來確認這一點——Solid 的下限更低。

渲染性能基準測試
我們使用 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 在重新渲染時一致更快,因為它不 diff 虛擬 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 正在批處理更新(這很好),但仍在每個批次上 diffing 比必要的 DOM 更多。
我們應該注意:React 19 的 useTransition 和自動批處理使這個相比 React 18 會更好。而且對於大多數真實應用程式,你不會推送每秒 100 次更新。在 10 更新/秒,兩個框架都很流暢。
具有 40 個欄位的複雜表單
| 指標 | React 19 | SolidJS 1.9 |
|---|---|---|
| 按鍵輸入延遲 | 2-4ms | <1ms |
| 表單提交渲染 | 28ms | 12ms |
| 每次按鍵的組件重新渲染 | 3-8 | 1 |
在 React 中,在一個欄位中輸入會導致父組件重新渲染,除非我們仔細記憶化一切。在 Solid 中,在欄位中輸入字面上只更新該欄位的 DOM 文本節點。沒有其他地方重新執行。
開發者體驗和生態系統
性能不是一切。你必須實際建立它,除錯它、為它招聘和維護它。
生態系統大小(截至 2025 年初)
| 因素 | 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 擴展,它還可以——你可以檢查信號圖,這實際上比 React 的組件樹視圖更有用於追蹤反應性 bugs。但它不那麼精緻。
TypeScript 支援
兩者都很出色。Solid 的 TypeScript 支援對於 JSX 實際上稍好一些,因為編譯器在構建時處理更多。我們在 Solid 程式碼庫中進行了較少的類型體操。
真正重要的生產權衡
在建立兩個版本並將 Solid 版本部署到暫存環境之後(客戶最終為生產環境選擇了 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:
- 你的應用程式有高頻率反應更新(儀表板、交易平台、實時協作)
- Bundle size 很關鍵(嵌入式小工具、微前端、移動優先)
- 你的團隊很小並願意投資於學習新的範式
- 你想要細粒度反應性而不與框架搏鬥
- 你正在建立新東西並且不需要大型組件庫生態系統
何時選擇 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 的 createResource 搭配 Suspense 真的很優雅。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> 不需要 keys,因為它透過參考追蹤陣列項目。當項目在陣列中移動時,Solid 移動實際的 DOM 節點。React 會卸載並重新掛載。這就是為什麼 Solid 的列表性能這麼好。
常見問題
SolidJS 在 2025 年準備好生產環境了嗎? 是的。Solid 1.x 已經穩定超過兩年。Cloudflare 和三星等公司已經在生產環境中使用它。核心庫是成熟和經過充分測試的。它周圍的生態系統(SolidStart、組件庫)比 React 的小,但正在穩步增長。問題不是 Solid 是否準備好——而是你的團隊和專案要求是否與其生態系統大小一致。
我可以從 React 遞增地遷移到 SolidJS 嗎? 不容易。儘管 JSX 語法相似,但運行時模型從根本上不同。你不能在 React 應用程式內部嵌入 Solid 組件(反之亦然),除非沒有 iframe 或 web 組件邊界。遷移基本上會是一次重寫。如果你考慮它,開始一個新的隔離功能或微前端,而不是嘗試轉換現有 React 程式碼。
SolidJS 支援伺服器端渲染嗎? 是的。SolidStart 提供 SSR、流式傳輸和伺服器函數。它是功能性的並快速改進,但截至 2025 年初,它仍然不如 Next.js 或 Remix 成熟。對於 SSR 重度專案,我們仍會推薦根據你的內容需要評估 Next.js 或 Astro。
React 19 的編譯器與 Solid 的方法相比如何? React 19 的編譯器(前身為 React Forget)自動記憶化組件以減少不必要的重新渲染。這是一個重大改進。但它仍在 React 重新執行組件函數和 diffing 虛擬 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 並破壞反應性。你會嘗試早期返回並想知道為什麼它們不工作。預算大約 1-2 週讓一位經驗豐富的 React 開發者在 Solid 中變得高效,再花 1-2 個月才能停止編寫 React 風格的 Solid。
哪個框架有更好的 TypeScript 支援?
兩者都有很好的 TypeScript 支援。Solid 有一個輕微的優勢,因為其編譯器可以在某些模式中提供較窄的類型(如 <Show> 中的回調),並且你不需要複雜 React hooks 有時需要的相同數量的通用類型參數。但老實說,兩者都很棒。這不應該是一個決定因素。
我應該將 SolidJS 用於我的下一個專案嗎? 這取決於你的限制。如果你是一個願意投資於學習曲線並繞過較小生態系統工作的小團隊,建立性能敏感的東西,Solid 是一個真正優秀的選擇。如果你需要雇用 React 開發者、使用已建立的組件庫或需要用於生產的經過實戰測試的元框架,React 是更安全的賭注。沒有通用的答案——只有適合你特定情況的正確答案。如果你想討論你的專案的決策,與我們聯繫,我們可以幫助你評估權衡。