核心原則

Inline CSS / JS 超過 ~30 行就值得拆出獨立檔案、走 Hugo resources.Get | minify | fingerprint 引入。 Template 變單純、editor 對 .css/.js 有 syntax highlight、minify 自動化、cache-busting fingerprint 自動處理。


為什麼 inline 有上限

商業邏輯

Inline CSS / JS 在 Hugo template 內看似省事(一個檔案搞定),但隨著規模上升出現多個成本:

規模Inline 的代價
< 10 行幾乎無 — 一目了然
10-30 行中 — Editor 不太能 highlight、template 開始混雜
30+ 行高 — 找東西要在 template 模式間切換、minify 沒做、cache 控制困難

拆檔的成本是「多 1-2 個檔案」、收益是「multiple」 — 過了 30 行門檻、ROI 已正向。

拆檔的實際得益

維度Inline拆檔 + Resources Pipeline
Editor syntax highlight部分 — 看 editor 是否支援 mixed mode完整 — 純 .css / .js 檔
Minify手動或 hugo template minifyHugo minify pipe 自動
Cache-busting手動加版本號fingerprint 自動
程式碼重用難 — 跟 template 綁容易 — 多 template 共用
Version control diff跟 template 改動混純檔案改動、清楚
測試可單獨測

這次任務的拆檔目標

觀察

layouts/_default/search.html 現況:

段落行數
Hugo template 與 HTML~30
Inline <script>~110
Inline <style>~80
總計~220

220 行的 single-file template、CSS / JS 各超過拆檔門檻 3-4 倍。

判讀

把 CSS 拆到 assets/search.css、JS 拆到 assets/search.js、template 只剩 HTML 結構與 Hugo 引入。

執行:拆檔步驟

Step 1:建立 assets 檔

1assets/
2├── search.css      # 原本 inline <style> 內容
3└── search.js       # 原本 inline <script> 內容

Step 2:template 引入

 1{{ define "main" }}
 2{{- $css := resources.Get "search.css" | minify | fingerprint -}}
 3<link href="{{ $css.RelPermalink }}" rel="stylesheet" integrity="{{ $css.Data.Integrity }}">
 4
 5<div data-pagefind-ignore class="search-shell">
 6  ...
 7</div>
 8
 9{{- $js := resources.Get "search.js" | minify | fingerprint -}}
10<script src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}" defer></script>
11{{ end }}

Step 3:JS 從全域 window.PagefindUI 改為 module 模式

如果原本 inline JS 用 new PagefindUI(...) 直接執行、拆檔後仍然可以這樣寫。但若想進一步,把 init 包成 function:

 1// assets/search.js
 2(function () {
 3  function init() {
 4    new PagefindUI({ ... });
 5    // ... rest of setup
 6  }
 7  if (document.readyState === 'loading') {
 8    document.addEventListener('DOMContentLoaded', init);
 9  } else {
10    init();
11  }
12})();

Step 4:清理 template

Template 從 220 行降到 ~30 行 — 只剩 HTML 結構。


內在屬性比較:四種引入方式

方式維護成本Cache 控制可重用性
Inline <style> / <script>中 — template 混雜自動跟著 template低 — 跟特定 template 綁
拆 .css / .js + 直接 link / script tag低 — 純檔案手動加版本號
Hugo resources.Get + minify內容變動觸發新 path
Hugo resources.Get + minify + fingerprint內容 hash 自動 cache-bust高 + 安全

優先選 fingerprint — Hugo 自動處理快取、瀏覽器看到內容變動的 fingerprint 一定 reload。


Hugo Resources Pipeline 的細節

resources.Get

1{{ $css := resources.Get "search.css" }}

assets/search.css。如果路徑下沒有、回傳 nil(要做 nil 檢查)。

| minify

去除空白、註解、合併 selector — 減少傳輸大小。

| fingerprint

對檔案內容做 hash、加到 URL(search.abc123.css)。內容變動時 fingerprint 變、瀏覽器把它當新檔案。

RelPermalink — site root 相對路徑(/search.abc123.css
Permalink — 完整 URL(https://site.com/search.abc123.css

通常用 RelPermalink 即可。

.Data.Integrity

Subresource Integrity hash — 給 integrity attribute 用、瀏覽器驗證下載內容沒被篡改。


拆檔的判斷門檻

Template 內含建議
0-10 行 inline CSS / JS不拆 — 維護成本最低
10-30 行視情況 — 有重用性需求就拆
30+ 行拆 — 各方面收益都正向
50+ 行強烈建議拆
多個 template 共用同一段立刻拆 — 重用性主導

當前 search.html 的 ~190 行 inline 程式碼遠超門檻、屬於「強烈建議拆」。


設計取捨:CSS / JS 引入策略

四種做法、各自機會成本不同。這個專案在 inline > 30 行時選 A(拆檔 + Hugo pipeline)當預設、其他做法在特定情境合理。

A:拆檔 + Hugo resources.Get | minify | fingerprint(這個專案的預設)

  • 機制:CSS / JS 拆到 assets/、template 用 resources.Get | minify | fingerprint 引入
  • 選 A 的理由:minify 自動、cache-bust 自動、editor syntax highlight、跨 template 重用
  • 適合:規模超過 30 行、預期長期維護的客製
  • 代價:多 1-2 個檔案、template 跟 assets 分屬兩處(grep 多一步)
  • 機制:拆檔到 static/assets/、template 直接 link
  • 跟 A 的取捨:B 簡單、A 自動處理 minify / fingerprint;B 改檔案後 cache 可能用舊版(要手動加版本號)
  • B 比 A 好的情境:簡單 prototype、確定不需要 cache-bust(純內部工具)

C:保持 inline

  • 機制:CSS / JS 寫在 template 的 <style> / <script>
  • 跟 A 的取捨:C 一個檔案搞定、A 拆兩個;但 C 在 30+ 行時 syntax highlight 失效、難維護
  • C 比 A 好的情境:< 10 行的小段、跟 template 邏輯緊密相關

D:CDN 引入第三方資源

  • 機制<script src="https://cdn.../lib.js">
  • 成本特別高的原因:依賴第三方可用性、跨域 CORS / SRI 處理、隱私問題(追蹤)
  • D 才合理的情境:第三方明確支援 SRI 且 CDN 是官方建議方式(少數 vendor library)

判讀徵兆

訊號拆檔動作
Template 內 <style> / <script> 超過 30 行拆到 assets/ 下對應 .css / .js
Editor 對 inline CSS / JS 沒 highlight拆檔讓 editor 套對應 mode
改 inline JS 後 cache 沒更新拆檔 + fingerprint 自動 cache-bust
同樣的 CSS / JS 在多個 template 重複拆出共用檔案
Inline 程式碼跟 Hugo template 邏輯混在一起難 grep拆檔讓 grep 範圍清楚

核心原則:Template 是 markup 的家、CSS / JS 是各自獨立檔案的家。三者混在一個檔案是過渡狀態、不是長期方案。