本文介紹 Hugo 的模板查找機制,查找機制決定每個頁面該套用哪個模板,主要由三個基本概念決定:page kind、page type,以及 front matter 手動指定的 layout。理解基本概念之後,我們會介紹完整的模板分類,才真正說明完整的查找順序,在文章的最後搭配兩個範例總結本文。
Page Kind
Hugo 定義了以下五種 kind:
home:網站首頁page:單一內容頁面,舊版稱作singlesection:區段列表頁,舊版稱作listtaxonomy:某個分類法(taxonomy)底下所有詞條(term)的列表頁,例如/tags/。若未提供對應的內容檔案,Hugo 會自動產生term:分類法底下單一詞條的頁面,例如/tags/hugo/。若未提供對應的內容檔案,Hugo 會自動產生
content/ 目錄結構與 page kind 的對應關係:
content/
├── _index.md # home
├── posts/
│ ├── _index.md # section
│ └── my-post/
│ └── index.md # page
└── tags/
├── _index.md # taxonomy(可選)
└── hugo/
└── index.md # term(可選)layouts/ 目錄結構與 page kind 的預設對應關係:
layouts/
├── home.html # home
├── section.html # section
├── taxonomy.html # taxonomy
├── term.html # term
└── page.html # pagePage Type
決定 page kind 之後,Hugo 接著會判斷 page type,用來決定同一種 kind 底下要套用哪一組模板。
舉例來說,同樣是 page kind,content/posts/ 與 content/movies/ 底下的文章,可以透過不同的 type 套用不同模板:
layouts/
├── movies/
│ ├── page.html # movies 專用的 page 模板
│ └── section.html # movies 專用的 section 模板
├── home.html
├── section.html
├── taxonomy.html
├── term.html
└── page.htmlType 的預設值等於內容所在的目錄名稱。例如 content/movies/p1/index.md 的 type 會預設為 movies。你也可以透過 front matter 的 type 欄位手動指定:
---
title: '我的文章'
type: 'posts'
---Layout
除了 kind 與 type,你也可以直接在 front matter 用 layout 欄位指定模板檔名:
---
title: '我的文章'
layout: 'custom'
---這會對應到:
layouts/
├── custom.html # 由 layout: custom 指定
├── home.html
├── section.html
├── taxonomy.html
├── term.html
└── page.htmllayout 欄位的優先權高於依 kind 或 type 自動判斷的結果。
三種分類方式的簡單理解:
- page kind 判斷頁面類型,例如標籤頁或一般文章頁
- page type 進一步細分,讓不同 type 套用不同模板
- layout 由 front matter 手動指定,適合 about、privacy 這類獨立頁面
模板分類
本段落介紹 layouts 目錄底下的模板分類,包含基礎模板、頁面模板等重要內容。
基礎模板
基礎模板就是 baseof.html,是所有頁面模板共用的外層架構,通常定義 html、head、body 等共通結構,以維持一致性,讓網站更容易維護。
基礎模板中通常會使用 block 函數,當符合條件時,block 函數就會使用頁面模板中的指定區塊渲染,條件為:
- 頁面模板需包含
define - 頁面模板不能包含能直接被渲染的內容
若不符合條件,基礎模板會被忽略,該檔案直接使用頁面模板渲染。基礎模板和頁面模板的使用範例如下:
<!DOCTYPE html>
<html lang="{{ site.Language.Locale }}">
<body>
<main>
{{ block "main" . }}
這段內容會被套用此基礎模板的頁面模板中,
對應的 define "main" 取代。
{{ end }}
</main>
</body>
</html>{{ define "main" }}
這段內容會取代基礎模板裡的 block "main"。
{{ template "inlineTemplate" }}
{{ end }}
{{/* 只允許空白、註解放在外部 */}}
{{ define "inlineTemplate" }}
Inline define 是被允許的,因為它不會被直接渲染。
{{ end }}layouts/baseof.html 和正確版本相同,但是頁面模板包含可以被直接渲染的文字。
{{ define "main" }}
這段內容無法取代 block,因為下方多了可直接渲染的內容(<!-- Foo -->),
導致 home 頁面不會套用 baseof.html,模板顯示空白。
{{ end }}
<!-- Foo -->頁面模板
頁面模板與 page kind 一一對應,常見的頁面模板包括:
layouts/
├── baseof.html
├── page.html # kind: page
├── home.html # kind: home
├── section.html # kind: section
├── taxonomy.html # kind: taxonomy
├── term.html # kind: term
├── single.html # page 的後備模板
├── list.html # home / section / taxonomy / term 的後備模板
├── all.html # 所有頁面模板的最終後備模板
├── _markup/ # 設定 Markdown 元素的渲染方式(render hook)
├── _shortcodes/ # 供內容頁面呼叫,不屬於頁面模板
└── _partials/ # 可重用區段,不屬於頁面模板其中:
- single 是 page 模板的後備選項
- list 是 home、section、taxonomy、term 模板的後備選項
- all 是所有頁面模板的後備選項
其他模板
除了頁面模板,Hugo 還包含下列用途較特定的模板類型:
- Sitemap:網站地圖
- RSS:訂閱資訊
- robots.txt:搜尋引擎爬蟲規則
- 404:找不到頁面時顯示的內容
Render hook
Render hook 讓你自訂 Markdown 指定元素轉換成 HTML 的方式,例如自訂圖片、連結、標題等元素的輸出結果。Render hook 放在 _markup/ 目錄下,支援以下類型:
_partials 目錄
layouts/_partials 底下放可重用的模板片段,用 partial 函數呼叫。以渲染 layouts/_partials/head.html 為例:
{{ partial "head.html" . }}第一個參數是模板名稱,二個參數(.)是傳入的 context。
_shortcodes 目錄
layouts/_shortcodes 底下放供 Markdown 呼叫的模板,用於在 Markdown 內容裡插入結構化元件,例如嵌入音訊、影片,或其他 HTML 區塊。
View
View 模板用於自動在不同頁面使用不同模板,而不是和 partial 一樣以指定的固定模板渲染。
View 模板必須以 .Render 渲染,查找規則與 Hugo 的模板查找順序相同。
查找順序
最基礎的優先級判斷如下:
- front matter 手動指定的
layout - 對應 page kind 的專用模板(home、section、taxonomy、term、page)
- page 的後備模板
single,或 home / section / taxonomy / term 的後備模板list all.html最終後備模板
這是只使用基礎概念的範例,實際上 Hugo 支援更複雜的查找規則:檔名可以用 . 分隔條件,且模板放置的目錄深度同樣會影響優先權。以下分別說明。
以 . 分隔條件
使用 . 分隔的檔名讓模板能同時過濾多個條件,包含語言、輸出格式、page kind 等等,在檔名中用 . 分隔各項條件即可:
home.rss.xml → 只用於首頁的 RSS 輸出
section.de.html → 只用於德語的 section 頁面
baseof.section.de.html → 只用於德語的 section 頁面的 baseof 基礎模板條件包含
- language: 語言
- role: 角色(見 Sites Matrix)
- version: 版本(見 Sites Matrix)
- outputformat: (見 輸出格式)
- mediatype: 媒體類型
- kind: 種類
- type: 類型
- layout: 佈局
在 v0.161.0 之後,你也可以用更精確的方式標記,比如 home._outputformat_rss_.xml、section._language_de_.html、baseof._kind_section_._language_de_.html。
不同模板支援的條件類型如下:
| 模板類型 | Page Kind | 輸出格式 | 語言 | 路徑深度 |
|---|---|---|---|---|
| 基礎模板 | 支援 | 支援 | 支援 | 支援 |
| 頁面模板 | 支援 | 支援 | 支援 | 支援 |
| Render hook | ❌ | 支援 | 支援 | 支援 |
| Shortcode | ❌ | 支援 | 支援 | ❌ |
| Partial | ❌ | ❌ | ❌ | ❌ |
| View | ❌ | ❌ | ❌ | ❌ |
路徑深度請見下方說明。
路徑深度
路徑匹配越接近目前渲染的內容,優先權越高,且路徑深度的優先權高於檔名條件,只有當兩者路徑距離相同時,才會回頭比較檔名條件的比對結果。
例如同時存在 layouts/movies/page.html 與 layouts/page.de.html,渲染 content/movies/ 底下的德語頁面時,page.de.html 雖然多比對了語言,但 movies/page.html 路徑更接近,Hugo 仍優先選擇 movies/page.html。
一個完整網站的範例
以下用兩個範例,展示本篇介紹的所有模板類型在實際專案中會如何組合出現。
簡單網站
最基礎的網站只需要一個基礎模板,搭配每種 page kind 各一份頁面模板。
layouts/
├── baseof.html
├── home.html
├── page.html
├── section.html
├── taxonomy.html
├── term.html
├── _markup/
│ └── render-image.html
├── _partials/
│ ├── header.html
│ └── footer.html
└── _shortcodes/
└── audio.html複雜網站
規模較大的網站,可能需要針對特定 type 或 section 使用專屬模板與 render hook,用 view 依情境渲染同一批內容的不同呈現方式。
layouts/
├── baseof.html
├── home.html
├── page.html
├── section.html
├── taxonomy.html
├── term.html
├── all.html
├── custom.html
├── movies/
│ ├── page.html
│ ├── _views/
│ │ └── card.html
│ ├── section.html
│ ├── _markup/
│ │ └── render-image.html
│ └── action/
│ ├── page.html
│ └── _markup/
│ └── render-image.html
├── films/
│ ├── _views/
│ │ └── card.html
│ ├── page.html
│ └── section.html
├── _markup/
│ └── render-image.html
├── _partials/
│ ├── header.html
│ └── footer.html
└── _shortcodes/
└── audio.html