快轉到主要內容

模板系統


本文介紹 Hugo 的模板查找機制,查找機制決定每個頁面該套用哪個模板,主要由三個基本概念決定:page kind、page type,以及 front matter 手動指定的 layout。理解基本概念之後,我們會介紹完整的模板分類,才真正說明完整的查找順序,在文章的最後搭配兩個範例總結本文。



Page Kind

Hugo 定義了以下五種 kind:

  • home:網站首頁
  • page:單一內容頁面,舊版稱作 single
  • section:區段列表頁,舊版稱作 list
  • taxonomy:某個分類法(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                    # page

Page 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.html

Type 的預設值等於內容所在的目錄名稱。例如 content/movies/p1/index.md 的 type 會預設為 movies。你也可以透過 front matter 的 type 欄位手動指定:

content/movies/p1/index.md
---
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.html

layout 欄位的優先權高於依 kind 或 type 自動判斷的結果。

提示

三種分類方式的簡單理解:

  1. page kind 判斷頁面類型,例如標籤頁或一般文章頁
  2. page type 進一步細分,讓不同 type 套用不同模板
  3. layout 由 front matter 手動指定,適合 about、privacy 這類獨立頁面

模板分類

本段落介紹 layouts 目錄底下的模板分類,包含基礎模板、頁面模板等重要內容。

基礎模板

基礎模板就是 baseof.html,是所有頁面模板共用的外層架構,通常定義 htmlheadbody 等共通結構,以維持一致性,讓網站更容易維護。

基礎模板中通常會使用 block 函數,當符合條件時,block 函數就會使用頁面模板中的指定區塊渲染,條件為:

  • 頁面模板需包含 define
  • 頁面模板不能包含能直接被渲染的內容

若不符合條件,基礎模板會被忽略,該檔案直接使用頁面模板渲染。基礎模板和頁面模板的使用範例如下:

layouts/baseof.html
<!DOCTYPE html>
<html lang="{{ site.Language.Locale }}">
<body>
  <main>
    {{ block "main" . }}
      這段內容會被套用此基礎模板的頁面模板中,
      對應的 define "main" 取代。
    {{ end }}
  </main>
</body>
</html>
layouts/home.html
{{ define "main" }}
  這段內容會取代基礎模板裡的 block "main"。
  {{ template "inlineTemplate" }}
{{ end }}

{{/* 只允許空白、註解放在外部 */}}

{{ define "inlineTemplate" }}
  Inline define 是被允許的,因為它不會被直接渲染。
{{ end }}

layouts/baseof.html 和正確版本相同,但是頁面模板包含可以被直接渲染的文字。

layouts/home.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 的模板查找順序相同。

查找順序

最基礎的優先級判斷如下:

  1. front matter 手動指定的 layout
  2. 對應 page kind 的專用模板(home、section、taxonomy、term、page)
  3. page 的後備模板 single,或 home / section / taxonomy / term 的後備模板 list
  4. all.html 最終後備模板

這是只使用基礎概念的範例,實際上 Hugo 支援更複雜的查找規則:檔名可以用 . 分隔條件,且模板放置的目錄深度同樣會影響優先權。以下分別說明。

. 分隔條件

使用 . 分隔的檔名讓模板能同時過濾多個條件,包含語言、輸出格式、page kind 等等,在檔名中用 . 分隔各項條件即可:

home.rss.xml           → 只用於首頁的 RSS 輸出
section.de.html        → 只用於德語的 section 頁面

baseof.section.de.html → 只用於德語的 section 頁面的 baseof 基礎模板

條件包含

v0.161.0 之後,你也可以用更精確的方式標記,比如 home._outputformat_rss_.xmlsection._language_de_.htmlbaseof._kind_section_._language_de_.html

不同模板支援的條件類型如下:

模板類型Page Kind輸出格式語言路徑深度
基礎模板支援支援支援支援
頁面模板支援支援支援支援
Render hook支援支援支援
Shortcode支援支援
Partial
View

路徑深度請見下方說明。

路徑深度

路徑匹配越接近目前渲染的內容,優先權越高,且路徑深度的優先權高於檔名條件,只有當兩者路徑距離相同時,才會回頭比較檔名條件的比對結果。

例如同時存在 layouts/movies/page.htmllayouts/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

無障礙設定

字體大小