Skip to main content

開發 Hugo 專案

開發 Hugo 專案是用「Hugo 語言」進行開發,這些語法全都是 Hugo 獨有,且這些語法都是用 Golang 實作的1。Golang 雖然是靜態語言,但是 Hugo 語言本身其實像是動態語言,完全不需要宣告變數型態。

開發環境設定

模板系統

沒接觸過模板的人可能有點搞不懂,就是 Hugo 透過這些模板直接渲染 HTML,因此 Hugo 模板沒有程式語言的那些上下文,會自己從模板順序依序往下渲染。Hugo 只在乎基礎模板,從 baseof.html 開始渲染,根據目錄結構找到 home.html(原 index.html)、page.html(原 single.html)等等,其他 partial 模板只是在呼叫時用到。

Hugo 透過 Go 語言的高併發特性多個頁面同時渲染,因此你無法設定不同頁面的頁面渲染順序,唯一能控制的只有輸出類型,你可以自定義 outputFormats 的 weight 以設定不同輸出類型的先後順序。

每個變數的 life cycle 只在當前模板,即使是同一個 HTML 頁面的不同模板,變數也會被清空,要在不同模板、不同頁面共享變數,唯一的方式只有 Store 方法。

模板查找

需要搞懂這幾個:

  1. 必須先理解前面講的頁面種類
  2. .Page.Path 也要搞懂,其實就是相對於 content 目錄的路徑
  3. Template types 是 Hugo 渲染頁面的基礎
  4. 現在你才能開始讀 template lookup order
  5. 相關的還有 New template systemoutput formats

模板使用

inline template/partial

請務必善用 inline template/partial 功能,這是 Hugo 唯一一個類似函式的東西。inline template 適用於同一檔案模組化和重用,雖然 inline template 全局可用,但是在全局用 inline template 會讓維護變的困難。

template and partial

partial 和 template 最大的差別是 partial 可以選擇回傳變數或者直接渲染 HTML 內容,template 只能直接渲染 HTML。

不推薦使用 partial 作為函式回傳變數,因為 partial 沒辦法清楚定義輸入輸出,也沒有 early-return/goto 等等機制,每個 partial 也限制只能有一個 return,這大幅限制維護和可讀性。

partialCached 不適用於 inline partial,只適用於獨立檔案的 partial。

block

block 定義模板後原地立刻執行,只是一個縮寫,使用頻率屈指可數的語法糖。

_markup 目錄

此目錄用於自訂特定元素的渲染管道 (render hook),例如圖片、heading 或程式碼塊等等。

變數

沒有要講什麼特別的,重要的是小寫 page site 可以直接存取目前頁面的 page 和 site,所以不要再寫一堆 dollar sign。

各種不同的 .Site 用法

取得全站物件有幾種方式

  • .Site
  • $.Site
  • site

前兩種一樣,多了 dollar sign 可以讓你從迴圈中取得「當前 template 的 context」,小寫的 site 則是「當前頁面的 site」,如果在迴圈或其他 context 語法裡面可能會取用到別的 site。

便箋 Scratch Pad

hugo.Store.Page.StorenewScratch 都有 scratch pad 功能,文檔是這樣介紹的:

Returns a globally scoped “scratch pad” to store and manipulate data.

為什麼要寫這種奇怪的內容?直白的寫跨 template 儲存變數不是很好嗎?完美體現文檔到底有多爛。

讀取設定檔

不支援讀取

Shortcode 語法

Shortcode 有兩種呼叫方式,standard notation {{< >}} 和 markdown notation {{% %}},區別是百分比符號的會在 「Markdown 文件本身渲染前」放到 Markdown,之後會跟著整個頁面一起再次被渲染,主要用於渲染 TOC 等頁面等級的功能。因為會再次被渲染,因此使用 {{% %}} 的 shortcode 內部不可使用 .Page.RenderString 或是 markdownify

Shortcode 出現換行

Trim space! Trim space! Trim space! Always remember to trim space!

Markdownify

我懶的找原文連結了,markdownify 現在就是 site.Home.RenderString 的 alias,文檔又不寫,到底有啥毛病?

.Page.RenderString 的差別是 scope,如果你的調用有頁面交互,那 markdownify 就不適用。

循環引用

無解,只能用戶自行避免。我在多個官方說法都看到同樣的結論。

偵錯

Hugo 除錯別無他法只能把變數印出來:

  1. {{ $var }}
  2. {{ debug.Dump $var }}
  3. {{ highlight (jsonify (dict "indent" " ") $var) "json" }}
  4. {{ site.Store }} + {{ site.Get }}
  5. {{ printf "type: %T, val: %s" $var $var }}
  6. {{ warnf "%s $var" }}
  7. 以上方案搭配 console.log 在瀏覽器印出

沒有任何其他方法,不用再想了。

條件判斷

andor 都是短路判斷cond 不是。

路徑判斷

專案設計應該永遠都使用 logical path(也就是檔案在 content 目錄的相對路徑)而不是 URL 判斷,因為 Hugo 有很強大的 URL 自訂功能,用硬編碼的 URL 判斷完全毀了這個功能。

自製額外輸出

Hugo 是模板語言因此輸出是預設的那些模板,要額外設定輸出,例如 JSON 文件或者 llms.txt,你要

  1. 建立 layouts/llms.txt
  2. Outputs 設定 home section 新增 llms
  3. outputFormats 設定 llms 區

全文教學請見 How to add llms.txt to a Hugo BlogAdding llms.txt & markdown output to your Hugo site,有了這兩個範例,其他輸出類型就可以依樣畫葫蘆完成。

如果額外輸出沒有渲染連結,請建立該輸出專用的 link render hook,比如建立了 home.fuse-search.json,他的 render hook 應該用 render-link.fuse-search.json,規則請見文檔

輸出 PDF

官方不支援,但是可以透過 resources.PostProcess + Pagedjs 自己做。

網站作者

請用官方推薦做法完成。建議完全放棄 .Params 設定網站作者,因為 .Params 方案無法設定多作者,未來要支援多作者就會造成出現兩套作者系統。

判斷頁面是否有 ToC

請用 {{ if in .TableOfContents "<li>" }} 判斷。

見微知著,Hugo 麻煩的地方就是連這種東西都要人發文問才知道最佳做法,而且所有人用法都不一樣,你就知道 Hugo 開發有多大的麻煩和混亂。我甚至看過有人直接比較字數,一個一個數空的 .TableOfContents 回傳 33 個字。

設定合併

Hugo 有些設定會深層合併,有些只有表層、有些不合併,見文檔

Footnotes

  1. Hugo 所有東西都是基於 Go Templates 為基礎延伸建立的函式、工具,刪減、修改了部分語法、又自創語法,因此稱他為 Hugo lang 更適合,只是這個 Hugo lang 是運行在 Golang/Go templates 之上的。