次世代 Hugo

無駄を削ぎ、本質を研ぐ

建立 JavaScript Partial

Sam Xiao's Avatar 2025-07-10

HTML在最後會載入 JavaScript,也是各 Template 都會重複使用之處,可將其抽成 Partial 方便維護。

Version

Hugo 0.147.8

JavaScript

assets/js/main.js

import { initHome } from './_home'
import { initSingle } from './_single'
import { initList } from './_list'
import { initAbout } from './page/_about'

document.addEventListener('DOMContentLoaded', () => {
  console.log('Hello from main.js!')
  initHome()
  initSingle()
  initList()
  initAbout()
})
  • 負責引用其他 JavaScript,本身不寫任何邏輯

JavaScript Partial

layouts/_partials/js.html

{{ with resources.Get "js/main.js" }}
  {{ $opts := dict
    "minify" (not hugo.IsDevelopment)
    "sourceMap" (cond hugo.IsDevelopment "external" "")
    "targetPath" "js/main.js"
  }}
  {{ with . | js.Build $opts }}
    {{ if hugo.IsDevelopment }}
      <script src="{{ .RelPermalink }}"></script>
    {{ else }}
      {{ with . | fingerprint }}
        <script
          src="{{ .RelPermalink }}"
          integrity="{{ .Data.Integrity }}"
          crossorigin="anonymous"></script>
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}
  • 負責載入 JavaScript

Line 1

{{ with resources.Get "js/main.js" }}
{{ end }}
  • resources.Get():從 assets 目錄取得 main.js
  • with():若 js/main.js 存在,將 目前 context 切到所 回傳物件 繼續 pipe

Line 2

{{ $opts := dict
  "minify" (not hugo.IsDevelopment)
  "sourceMap" (cond hugo.IsDevelopment "external" "")
  "targetPath" "js/main.js"
}}
  • dict():整理 js.Build() 所需 map 參數

    • minify開發模式false生產模式 時則 true

    • sourceMap開發模式產生生產模式不產生

    • targetPath:最終產出為 js/main.js

Line 7

{{ with . | js.Build $opts }}
{{ end }}
  • js.Build():將 目前 context 傳入 js.Build $opts
    • 目前 contextjs/main.js 物件
    • js.Build $opts 為 partial function,因此可將 . 傳入
  • with():若 js.Build() 成功,則將 目前 context 切到 回傳物件 繼續 pipe

Line 8

{{ if hugo.IsDevelopment }}
  <script src="{{ .RelPermalink }}"></script>

開發模式 下處理 JavaScript:

  • RelPermalink:直接從 目前 context 取得 其相對路徑
    • 目前 contextjs.Build() 回傳物件

Line 10

{{ else }}
  {{ with . | fingerprint }}
    <script
      src="{{ .RelPermalink }}"
      integrity="{{ .Data.Integrity }}"
      crossorigin="anonymous"></script>
  {{ end }}
{{ end }}

生產模式 下處理 JavaScript:

  • fingerprint():將 JavaScript 檔名加上 hash,避免被連覽器 cache
    • 目前 contextjs.Build() 回傳物件
  • with():若 fingerprint() 成功,則將 目前 context 切到 回傳物件 繼續 pipe
  • RelPermalink:直接從 目前 context 取得 其相對路徑
  • Data.Integrity:提供 SRI,用來讓瀏覽器驗證資源內容是否被竄改,需搭配 crossorigin="anonymous"
    • 目前 contextfingerprint() 回傳物件

Baseof Template

layouts/baseof.html

<!doctype html>
<html lang="en">
  <head>
    {{ partial "css.html" . }}
  </head>
  <body>
    {{ block "main" . }}{{ end }}
    {{ partial "js.html" . }}
  </body>
</html>
  • 所有 template 基礎的 baseof.html

Line 8

{{ partial "js.html" . }}
  • <body> 內引用 js partial

Conclusion

  • JavaScript 要處理的東西看似簡單,這原本都要靠 Gulp、Webpack、Vite 之類工具處理,但這些 Hugo 都已經內建,只需 Pipe 即可,可將這些處理過程整合在 js Partial 內
  • 之所以要朝狀使用 with(),因為每個 Function 執行會成功或失敗,只有成功後才會將 目前 Context 切到 回傳物件 繼續 Pipe
  • 此段處理 JavaScript 流程摘自 Hugo 官網,極具參考價值,是非常優秀的 Go template 寫法