Hugo SPA-like 頁面切換:Swup、Turbo、Barba 套件選擇分析
本文介紹 Hugo 建立 SPA-like 網站最適合的 JS 套件是什麼。不只 Hugo,靜態網站如 Astro/Jekyll/Eleventy/Pelican 這篇文章基本上都成立。
本文提到的推薦與不推薦都是基於個人網站、文檔網站等重內容的網站,不同網站有各自考量點,缺點也可能變優點。
目的
首先說本文想達到的目的是什麼,就是架設像 Docusaurus/Vitepress 這種瀏覽流暢的網站,他們的流暢主要來自於各自使用的 router (react router/vue router) 用 JS 做到頁面部分替換,本文的目的就是在 Hugo 做到這種網站。
由於 Hugo 是傳統的 MPA,架設的網站也不會有伺服器1,都是 host 在 Github/Cloudflare/Netlify 之類純粹提供靜態 HTML 的網站服務。而 SPA 要嘛是有伺服器提供替換的頁面片段,要嘛是把網頁做的像 Docusaurus 這種雖然本體是靜態 HTML,但是加上 React Router 之後變成大型 React APP,網站會變的很重、很複雜。
綜合以上,要在 Hugo 做到 SPA-like 只能用 DOM swap 的 JS 工具,現代的 react/vue 應該直接用他們的生態開發而不是把他們放到 Hugo 裡面搞自己,而相關的 JS 工具整理表格如下:
| 套件 | GitHub Stars | npm 週下載量 | 套件大小 (gzip) | 維護狀態 | 主要機制 |
|---|---|---|---|---|---|
| Swup.js | 5.2K | 26.8K | ~7 kB | 🟢 | AJAX + CSS transition |
| @barba/core | 12.9K | 4.5K | ~9.6 kB | 🟡 | AJAX + JS/CSS hook |
| @hotwired/turbo | 7.3K | 744K | ~43.9 kB | 🟢 | Drive / Frames / Streams |
| Unpoly | 2.8K | 2.9K | ~57.6 kB | 🟢 | 片段更新 + layer |
| htmx | 48.2K | 146K | ~16.2 kB | 🟢 | 任意元素觸發 AJAX swap |
| Highway.js | 1.4K | 0.4K | ~2.4 kB | 🔴 | - |
| @unseenco/Taxi.js | 0.6K | 1.5K | ~3KB | 🟢 | - |
整理時間為 2026/06,套件大小用 esbuild bundle 過後量測。
Swup.js
Swup.js 攔截連結點擊,用 fetch 取得下一頁的完整 HTML,然後只替換 JS 指定的 DOM container,核心是生命週期 hooks(willReplaceContent, contentReplaced 等),讓你能在替換前後插入邏輯。Swup 不帶任何伺服器協定,完全是純前端 DOM 操作。
完美符合 Hugo 使用需求,有大量插件且開發活躍,是最適合的套件。
hotwired/turbo
本質是 Rails 生態系的前端協定實作,分為三層:Turbo Drive(整頁切換加速)、Turbo Frames(局部 frame 替換)、Turbo Streams(伺服器推送局部更新)。
Turbo Drive:
- 替換整個
<body>,merge<head> - 有完整 event hook 系統(
turbo:before-visit、turbo:before-render、turbo:before-fetch-request等) turbo:before-render可以完全自訂 render function,甚至換成 idiomorphdata-turbo-permanent保留特定元素跨頁不替換data-turbo-track="reload"處理 asset 版本變更data-turbo="false"逐元素退出- 內建 prefetch(v8 預設開啟)和 preload
Turbo Frames:
- 從完整 HTML 回應中抽取對應
<turbo-frame id="">的內容替換當前 frame - 不需要伺服器回傳局部片段(前對話已確認)
- 但來源頁與目標頁都需要存在對應 ID 的
<turbo-frame>標籤
Turbo Streams:
- 需要伺服器回傳
Content-Type: text/vnd.turbo-stream.html,在靜態網站無法實現
Turbo Frames 類似於 Swup,Drive 則是預設替換整個 body 標籤,我認為兩者都不是很適合 Hugo。
barba.js
與 Swup 非常接近,攔截連結、fetch 下一頁、替換 container,同樣純前端,不需要伺服器配合。
核心架構差異在於 hook 的組織方式。Swup 是集中式 hook,所有頁面切換共用同一組生命週期,你在 callback 裡自行判斷當前情境。Barba 是分散式 transition 物件:你宣告多個 transition,每個物件描述自己的比對條件(從哪個 namespace 來、去哪個 namespace)。這個差異決定了各自的適用情境,如果你的網站不同頁面類型之間需要差異化的處理邏輯,例如設計工作室、品牌形象站、作品集這類以視覺體驗為核心的站,Barba 的分散式結構讓頁面切換邏輯自成一體,比在 Swup 的集中 hook 裡寫 if/else 更易維護。反之,如果你的 Hugo 站屬於部落格、文件站、個人網站這類頁面結構相似、切換行為一致的類型,Barba 的分散式架構只是多餘的複雜度,Swup 更合適。
此外 Barba 已超過兩年未更新。
Unpoly
Unpoly 是一個完整的「server-rendered app 增強框架」,它的設計哲學是任何連結、任何表單、任何 DOM 元素都可以透過 HTML 屬性(up-target、up-layer 等)宣告式地定義「這個互動要更新哪裡」。它帶有 layer(overlay/modal)系統、fragment 更新、request 快取、樂觀更新等完整機制,是四個裡面功能最重的框架。
比如 conditional-requests、optimizing-responses、caching、context、layer-terminology ... 等等機制都預設有一個可以配合的伺服器,這在 Hugo 網站不存在。
過重,過多不適用功能,因此不推薦。
不適合的
- htmx 設計本質是要求伺服器會回傳 DOM 片段,因此不適合。
- Highway.js 停止維護,不適合。
- taxi.js 是 Highway.js 的後繼者,用戶少,文檔少,風險過大不適合。
- pjax 停止維護更久,因此連表格都不包含他。
View transition
這根本就不是 SPA,不要再鬼扯了笨蛋 AI,AI 總是會扯到他就可以知道現在的 AI 邏輯思考能力依然是零。View transition 的原理是頁面切換時,瀏覽器會先擷取舊畫面快照,再渲染新畫面,透過 CSS 動畫在兩個狀態間平滑過渡,和 SPA 一點關係都沒有,連 SPA-like 都扯不上邊。