Skip to main content

條件式載入資產,判斷 Hugo 頁面中是否包含指定元件(HasShortcode, .Store, .Scratch)

條件式載入資產是老生常談的問題,然而官方文檔依舊寫的七零八落,不自己摸索根本不能直觀的理解該怎麼做。本文介紹 Hugo 所有能用的頁面條件判斷方式,用於判斷頁面是否有指定的東西,包含

  • HasShortcode
  • .Store / .Scratch

目前只有這兩種方式可以跨模板判斷。

HasShortcode

HasShortcode 可以在模板的任何位置判斷當前頁面是否包含 shortcode,因為 Hugo 會先過一次 Markdown 文件,Markdown 之外的 shortcode 呼叫如 .Page.RenderString 則理所當然的不會被判斷。相關源碼在 page__content.go#L108-L124

使用:

<!-- partial A -->
{{ if .HasShortcode }}
{{ with resources.Get "css/foo.css" }}
...
{{ end }}
{{ end }}

Store vs Scratch

.Scratch 已經被淘汰,他現在就是 .Store 的別名。

Store and Scratch

.Store.Set / .Get 依賴於模板執行順序,不會提前 evaluate,因此你必須在所有最後的模板才用 .Store.Get "hasXXX" 來判斷是否載入該元件的資源。

使用:

<!-- partial A -->
{{ if .Param "someFunction" }}
{{ .Store.Set "func" true }}
{{ end }}
<!-- partial B -->
{{ if .Store.Get "func" }}
{{ with resources.Get "css/foo.css" }}
...
{{ end }}
{{ end }}

Shortcode + Store

shortcode 內部呼叫 .Store:這時候可以透過 trigger .Content 提前 evaluate Markdown,這樣就可以在 .Content 真正渲染前使用 .Get 方法。trigger 方式如文檔。注意只有由 Markdown 觸發的 .Store .Scratch 才有用如,因為我們是在 trigger .Content evaluate。

Store 的缺點

在 live reload 會有問題,因為重新載入之後 .Store 的 flag 狀態會保持 true 不會被重置為 false,造成只要 live reload 一次之後資產(CSS/JS)就不會被載入了。實際情況是只有當前刷新的頁面會被完整重新渲染,其他頁面則會因為部分刷新而缺少資產。

這是一個非常久且公認存在的 bug,直到當前版本 0.164.0 都持續存在。

Content 快取

觸發 .Content 提前渲染會不會造成任何問題?答案是不會。.Content 會被快取1,同一個 Page 在不同模板呼叫 10 次 .Content 都沒差,都是被快取的,但是文檔又沒講,到底為什麼連這麼重要的問題都是 undocumented?

Footnotes

  1. pageContentOutput.Content() 實際呼叫的是 getOrCreateScopehugolib/page__content.go#L143),內部用 hmaps.Cache 做 get-or-create,key 存在就直接回傳既有結果,不會重新渲染。快取物件 cachedContent 也寫「Content cached for a page instance」,且由每個 page 各自的 pageMeta 建立,不是全站共用一份。key 組成是 scope + 輸出格式名稱,所以同一個 page 若有多種 output format(html/amp/json),會各自獨立快取,彼此不共用。