ZSL

Hugo 開發的實用知識


本文彙整開發 Hugo 時一定會用到,但是很難在文檔中找到,或是文檔根本就沒寫的知識,主要是在 Hugo 論壇 挖到的資訊。

Shortcode Markdown Notation

Shortcode 有兩種呼叫方式,{{< >}} {{% %}},區別是百分比符號的會在 Markdown 渲染前預先處理,因此有些奇怪的問題就要靠他解決,例如在 ToC 加上 icon

Shortcode 出現換行

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

Nested Shortcode

這是老生常談的問題,然而我也不是很懂他到底該怎麼做,因為如果這個問題很單純就不會有一堆人問,maintainer 回答也不會每次都是一長串對話。

頁面內容判斷

hasShortcode 可以判斷頁面是否使用指定 shortcode,而且在 .Content render 之前就可以判斷,但是 .Store 方式不行。

要 trigger .Content evaluate 可以使用 {{ $noop := .WordCount }} 完成。

循環引用

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

偵錯

  1. {{ debug.Dump . }}
  2. {{ highlight (jsonify (dict "indent" " ") $MAP_VARIABLE) "json" }}
  3. {{ site.Store }} + {{ site.Get }}
  4. {{ printf "type: %T, val: %s" $VAL $VAL }}

效能

檢測:hugo --templateMetrics --templateMetricsHints

優化:通常都是頁面又 walk 一遍其他頁面導致 O(N^2) 複雜度,不然預設 O(N) 加上 Hugo 平行處理的機制沒什麼好優化了,頂多就是把檢測找到的複雜模板改用 partialCached 快取處理,然後資料結構基本觀念要有,不要大型數據還在用 range 跑

條件判斷

andor 都是短路判斷cond 不是。

路徑判斷

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

行內 define

我不知道這實際上到底該叫什麼名字,因為論壇上所有人講的都不一樣。下方介紹行內 define 會分成資訊、總結和測試三個段落。

資訊

總之你可以在檔案內寫 define 作為一個 local scoped function 來使用,但是有幾個問題:

  1. 模板沒有「宣告」的先後順序問題,這個說法是錯的,下一點解釋。
  2. Maintainer 說:模板有 “parse time",也就是說所有動作執行之前會先解析所有 templates,等一下的測試會證實這點。
  3. 行內 define 的模板全局可用,因此要避免撞名。
  4. Maintainer 說:template 和 partial 除了支援 partialCached 以外基本沒差,主要是 template 語義上屬於 local macro。
  5. Maintainer 說:inline partial 應該永遠加上 .html suffix,但我有用和沒用感覺一樣。

總結

  1. 行內 define 無須擔心順序問題,請見下方測試。
  2. 行內 define 無順序問題,但是取用 .Store 內容還是要注意頁面渲染順序。
  3. 行內 define 用於 macro 用途,也就是單模板內部用的小工具,只是要避免撞名。
  4. partial 是 Hugo 做的工具,template 是 Go 原生。
  5. partial 和 template 的差別是 return / partialCached 功能,否則沒差。
  6. 至於為什麼這個有問題?我也不知道。

測試

define 順序測試

只需要這些檔案測試,所有測試都正常渲染沒有順序問題。

❯ tree
.
├── content
│   └── _index.md
├── layouts
│   ├── _partials
│   │   └── foo.html
│   └── home.html
└── hugo.toml

hugo.toml

baseURL = 'https://example.org/'

disableKinds = ["page", "taxonomy", "section", "term", "rss", "sitemap"]

_index.md:

+++
title = 'Home'
+++

home.html:

{{ partial "foo.html" . }}

foo.html:

Try render test1-1 with template: {{ template "test1-1" . }}
Try render test1-2 with partial: {{ partial "inline/test1-2" . }}
<br>
<br>
<br>
Try render test2-1 with template recursion: {{ template "test2-1" (dict "level" 1) }}
Try render test2-2 with partial recursion: {{ partial "inline/test2-2" (dict "level" 1) }}

{{- define "test1-1" -}}
    test1-1<br>
{{- end -}}

{{- define "_partials/inline/test1-2" -}}
    test1-2<br>
{{- end -}}

{{- define "test2-1" -}}
    {{ $level := .level }}
    {{ printf "Level %d<br>" $level | safeHTML }}
    {{ if le $level 2 }}
        {{ template "test2-1" (dict "level" (add $level 1)) }}
    {{ end }}
{{- end -}}

{{- define "_partials/inline/test2-2" -}}
    {{ $level := .level }}
    {{ printf "Level %d<br>" $level | safeHTML }}
    {{ if le $level 2 }}
        {{ partial "inline/test2-2" (dict "level" (add $level 1)) }}
    {{ end }}
{{- end -}}

自製額外輸出

全文教學請見 How to add llms.txt to a Hugo BlogAdding llms.txt & markdown output to your Hugo site

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

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

有了這兩個範例,其他輸出類型就可以依樣畫葫蘆完成。

輸出 PDF

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

設定 syntax highlighting

有幾種方案完成。

  1. markup 設定 noClasses true,就可以直接用 style 設定 Chroma styles

  2. 設定 noClasses false,然後 CSS 使用 hugo gen chromastyles 完成

    hugo gen chromastyles --style=solarized-light | sed 's/\./html.dark ./' >> assets/css/chroma.css
    hugo gen chromastyles --style=onedark | sed 's/\./html:not(.dark) ./' >> assets/css/custom.css
    

    這設定了 html.dark 方式指定的 light mode/dark mode Chroma style。修改 sed 指令也可以套用到 data-theme 方式的 dark mode。

  3. 外部 JS,使用 highlight.js/prism.js,網路上教學很多,不贅述也不推薦,因為拖累客戶端的效能。

  4. shiki syntax highlighting,這和外部 JS 一樣是修改已經構建完成的 HTML,最大的差別是 shiki 採用 VS Code 引擎因此更正確,還有 shiki 是伺服器端渲染,完全沒有客戶端效能問題。有兩種方案

    1. 使用 rehype 方案,速度很慢
    2. 自製 Node JS 腳本,速度可加快超過百倍

翻譯回退 (language fallback)

使用 module.mounts 完成,這是官方推薦做法lang.Merge 只幫你找到頁面不會渲染。


Related Articles