Skip to main content

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 Starsnpm 週下載量套件大小 (gzip)維護狀態主要機制
Swup.js5.2K26.8K~7 kB🟢AJAX + CSS transition
@barba/core12.9K4.5K~9.6 kB🟡AJAX + JS/CSS hook
@hotwired/turbo7.3K744K~43.9 kB🟢Drive / Frames / Streams
Unpoly2.8K2.9K~57.6 kB🟢片段更新 + layer
htmx48.2K146K~16.2 kB🟢任意元素觸發 AJAX swap
Highway.js1.4K0.4K~2.4 kB🔴-
@unseenco/Taxi.js0.6K1.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-visitturbo:before-renderturbo:before-fetch-request 等)
  • turbo:before-render 可以完全自訂 render function,甚至換成 idiomorph
  • data-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-requestsoptimizing-responsescachingcontextlayer-terminology ... 等等機制都預設有一個可以配合的伺服器,這在 Hugo 網站不存在。

過重,過多不適用功能,因此不推薦。

不適合的

  1. htmx 設計本質是要求伺服器會回傳 DOM 片段,因此不適合。
  2. Highway.js 停止維護,不適合。
  3. taxi.js 是 Highway.js 的後繼者,用戶少,文檔少,風險過大不適合。
  4. pjax 停止維護更久,因此連表格都不包含他。

View transition

這根本就不是 SPA,不要再鬼扯了笨蛋 AI,AI 總是會扯到他就可以知道現在的 AI 邏輯思考能力依然是零。View transition 的原理是頁面切換時,瀏覽器會先擷取舊畫面快照,再渲染新畫面,透過 CSS 動畫在兩個狀態間平滑過渡,和 SPA 一點關係都沒有,連 SPA-like 都扯不上邊。

Footnotes

  1. 一般用戶通常不會有,但是實際上還是有很多人會把自己的網站在 homelab/VPS 上用 nginx 等工具架設。