# HTML-first 還是 SPA：把預設調回來，不是打聖戰

> 預設選 HTML-first，先問狀態活在 server 還是 client、互動是換內容還是操作大量本地狀態，真的需要 client 狀態的畫面再升級到 SPA。

原文：https://sheng.page/posts/html-first-vs-spa-choosing-the-default/ · 發布：2025-09-20 · 作者：Sheng

每次有人問「這個專案要不要上 SPA」，我都想先反問一句：你這畫面的狀態，到底主要活在 server 還是 client？多數人答不出來，因為從來沒想過這件事，反射性就 `create-vue`、`create-react-app` 開下去了。預設錯位才是真正的問題，不是 SPA 本身有罪。

htmx 2.0 在 2024 年中發布之後，server-first 這條路又被重新拿出來討論。它沒帶來什麼新魔法，核心從第一天就沒變：送 HTML 片段、狀態留 server、瀏覽器局部換 DOM。htmx 真正的價值，是逼你問一個一直被跳過的問題：這塊互動，真的需要把狀態搬到前端嗎？

## 狀態活在哪，答案就在哪

我給自己的判準很簡單，就兩個問題。第一，這個畫面的狀態主要活在 server 還是 client？第二，使用者的互動是「換掉一塊內容」還是「即時操作大量本地狀態」？

CRUD、表單與 workflow-heavy 後台的狀態幾乎都活在 server，使用者送出一個 request，後端算完回一段 HTML，畫面跟著更新。這種場景硬套 SPA，等於多出一層 JSON API、一個 state store、前後端兩套 model 的同步，以及 serialise/deserialise，只為了處理原本用 form submit 就能完成的「新增一筆資料」。

server-first 在這裡有清楚的責任邊界：狀態只有一份，留在 server，前端只呈現結果。這樣不用處理兩邊不一致或 client cache 過期，也不用為了一個下拉選單寫 reducer。htmx、Hotwire 的價值就在於保留 HTTP 與 DOM 的直覺，改一個按鈕之前不必先接受整套框架世界觀。

## 真的需要 client 狀態的那些場景

但話講回來，我不是要否定 SPA。有一類東西，狀態天生就活在 client，你把它硬塞回 server 才是災難。

高互動的編輯器、canvas 繪圖、拖拉排版這種，每一個滑鼠移動都是狀態變化，你不可能每動一下就發 request。離線優先的應用更不用說，使用者根本沒有網路，狀態只能先活在本地。還有現在很多 local-first 的 SaaS，資料寫進 IndexedDB，記憶體裡維護一份狀態，CSR 都能做到接近原生的流暢度，這種你用 server-first 是自找麻煩。

判準仍是那兩個問題。互動若要即時操作大量本地狀態，狀態自然放在 client，這時用 Svelte 或 React，再以 runes 或 signals 管理反應式狀態，成本付得合理。選擇應該來自畫面的需求，不該只是手滑開了 `create` 指令。

我自己踩過的坑很實在。有個專案一開始用 SvelteKit SSR，跑起來在某些環境就是慢，最後改成純 SPA 反而流暢，因為那個畫面互動密集、狀態原本就該在前端。反過來也有，後台管理介面我堅持用 server routes 當 SSR + BFF，外部請求不直接碰資料層，省下的前端複雜度多到不像話。同一個人、同一年，兩個相反的決定，差別只在那兩個問題的答案不同。

## 把預設調回 HTML-first

我想講的從頭到尾就一件事：預設值。反射性開整套 SPA 是預設錯位，你跳過了判斷這一步。把預設調回 HTML-first，意思是你一開始假設狀態活在 server、互動是換內容，先用輕量一層的方式做，真的遇到需要重本地狀態的畫面，再升級到 client。

一個專案可以讓兩種模式並存，後台 CRUD 走 htmx，高互動編輯器走 SPA，各自負責適合的畫面。若全部塞進同一套重型框架，每個簡單表單都得多付 SPA 的成本；預設先選便宜的做法，有需要再升級，複雜度才花在真正有用的地方。


## 補充筆記

- 先問兩個問題：狀態主要活在 server 還是 client，互動是換內容還是操作大量本地狀態。
- CRUD、表單、workflow-heavy 後台狀態活在 server，server-first 讓責任邊界清楚、只維護一份狀態。
- 高互動、離線優先、canvas、local-first 這類狀態天生活在 client，別硬塞 htmx。
- 反射性開整套 SPA 是預設錯位，跳過了判斷這一步；把預設調回 HTML-first，需要才升級。
- 同一個專案可以兩種模式並存，複雜度該花在真正需要的畫面上。
> 想法與技術判斷出自 Sheng，和 Claude 一起起草 · 列入 20260613 blog 翻新計劃，新漆未乾。