Hugo 模板系统概述

Hugo 是一个以速度著称的静态网站生成器,其强大的主题(Theme)**系统是其核心魅力之一。一个好的主题不仅能让网站拥有美观的界面,更能通过其合理的**文件结构查找机制,为开发者提供极大的灵活性和可维护性。本文将深入探讨 Hugo 主题的内部结构,并揭示其文件查找的奥秘。

Hugo 主题的“骨架”:约定优于配置

Hugo 主题的核心在于其遵循**约定优于配置(Convention over Configuration)**的原则。这意味着,虽然你不需要在配置文件中显式声明每个文件的用途,但将文件放置在特定的目录并赋予它们特定的名称,Hugo 就能自动识别并加载它们。这种机制大大简化了开发流程,同时保证了主题的规范性和可扩展性。

一个典型的 Hugo 主题通常包含以下几个关键目录和文件:

  • archetypes/: 原型文件的存放地,用来默认添加文件的 meta data。当你使用 hugo new <content-path> 命令创建新内容时,Hugo 会根据这些原型来填充内容的 Front Matter。最常见的如 archetypes/default.md。比如我的 default.md 内容是如下的内容:

    ---
    title: "{{ replace .Name "-" " " | title }}"
    date: {{ .Date }}
    draft: true
    tags: ["Default"]
    ---

    那么我每次使用 hugo new post/test.md 这样的命令创建一个新的文件的时候,上面的内容会自动插入到这个文件的头部。

  • assets/: 存放原始静态资源。这些文件通常需要经过 Hugo Pipes 处理,例如 SCSS 源文件、未压缩的 JavaScript、原始图片等。通过管道处理后,它们可以被优化、编译并输出到最终的 public 目录。

  • layouts/: 主题的核心,所有模板文件都集中于此 (重要)。

    • _default/: 存放默认布局模板。这是网站最基础的模板,当没有更具体的模板可用时,Hugo 会回退到这里查找。常见的包括:
      • baseof.html: 定义了页面的基本 HTML 骨架(<html>, <head>, <body>)。
      • list.html: 默认的列表页面模板,用于渲染文章列表、分类列表、标签列表等。
      • single.html: 默认的单内容页面模板,用于渲染独立的文章、页面等。
      • 404.html: 404 错误页面模板
      • rss.xml, sitemap.xml, jsonfeed.json: 分别用于生成 RSS Feed、站点地图和 JSON Feed。
    • 特定类型目录(如 posts/, projects/):如果你希望为特定类型的内容(由 Front Matter 中的 type 字段定义)提供专属布局,可以在此创建对应的目录。例如,layouts/posts/single.html 会覆盖 layouts/_default/single.html 来渲染 posts 类型的内容。
    • partials/: 存放可重用的模板片段。这些是可以在其他模板中通过 {{ partial "filename.html" . }} 调用的代码块,例如页眉 (header.html)、页脚 (footer.html)、导航菜单 (nav.html)、侧边栏 (sidebar.html) 等。
    • shortcodes/: 存放短代码模板。短代码允许你在内容文件中嵌入动态的、可重用的内容块。例如,layouts/shortcodes/image.html 可以创建一个用于插入图片的短代码。
  • static/: 存放直接复制的静态文件。这些文件在网站构建时会被直接复制到最终的 public 目录,不会经过 Hugo 的任何处理。这通常包括图片、CSS 文件、JavaScript 文件、字体文件等。

  • data/: 存放结构化数据文件(YAML, JSON, TOML, CSV)。这些数据可以在模板中通过 .Site.Data 访问,常用于存储配置信息、导航菜单数据等。

  • i18n/: 存放多语言翻译文件。用于网站的国际化(i18n),通常是 YAML 或 TOML 格式的键值对。

  • exampleSite/: 可选目录,通常包含一个完整的示例 Hugo 网站,展示主题的用法和配置,对于主题用户非常有帮助。

  • theme.toml: 主题的元数据文件,包含主题的名称、作者、描述、许可证等基本信息。

魔法背后的逻辑:Hugo 的文件查找顺序

Hugo 之所以能如此智能地渲染页面,关键在于其严谨的文件查找优先级。这个原则可以概括为:“项目目录优先于主题目录,最具体的文件优先于最通用/默认的文件”

让我们通过几个关键场景来理解这个查找顺序:

  1. 项目文件覆盖主题文件(高优先级)
    • 这是最重要的原则。当你的 Hugo 项目根目录下的任何文件(例如 layouts/static/data/ 中的文件)与当前激活主题中的同名文件存在于相同路径时,Hugo 总是优先使用你项目根目录下的文件
    • 这意味着你可以轻松地定制和覆盖主题的任何部分,而无需直接修改主题文件本身。 这对于主题的更新和维护至关重要,因为你可以在不影响定制内容的情况下升级主题。
  2. 模板查找优先级:从具体到通用
    • 对于 layouts/ 目录下的模板文件,Hugo 会尝试找到与当前页面最匹配的模板。查找顺序遵循以下模式:
      • 最具体的路径和名称:例如,对于一个类型为 posts,布局为 featured,语言为 en,输出格式为 html 的单页内容,Hugo 会按以下顺序尝试查找:
        1. layouts/posts/featured.html (项目)
        2. layouts/posts/single.html (项目)
        3. layouts/_default/featured.html (项目)
        4. layouts/_default/single.html (项目)
        5. themes/<THEME_NAME>/layouts/posts/featured.html (主题)
        6. themes/<THEME_NAME>/layouts/posts/single.html (主题)
        7. themes/<THEME_NAME>/layouts/_default/featured.html (主题)
        8. themes/<THEME_NAME>/layouts/_default/single.html (主题)
      • baseof.html 的特殊性baseof.html 是页面渲染的“骨架”,它在其他内容模板(如 single.htmllist.html)被填充之前就已经被加载。它通常定义了 {{ block "main" . }} 等占位符,由内容模板通过 {{ define "main" }} 来填充。其查找顺序也遵循“项目优先,_default 优先于特定类型”的原则。
  3. 部分模板(Partials)的查找
    • 当你使用 {{ partial "header.html" . }} 调用一个部分模板时,Hugo 会首先在项目根目录的 layouts/partials/ 中查找 header.html
    • 如果找不到,它才会去当前主题的 layouts/partials/ 目录中查找。
  4. 短代码(Shortcodes)的查找
    • 与部分模板类似,当你使用 myshortcode 调用一个短代码时,Hugo 会首先在项目根目录的 layouts/shortcodes/ 中查找 myshortcode.html
    • 如果找不到,则会去当前主题的 layouts/shortcodes/ 目录中查找。
  5. 静态文件(Static Files)的合并
    • 来自你项目根目录下的 static/ 文件夹的文件,以及来自主题的 static/ 文件夹的文件,都会被复制到最终的 public/ 目录。
    • 如果存在同名文件,项目根目录下的文件会覆盖主题中的文件。

如何修改模板,让它支持纯 html 内容

上面的描述简单的说明了 hugo 主题的结构,以及查找过过程。

我在使用的时候,一般是直接写 markdown 文件,然后让它渲染成 html。但是有的时候我想直接写 html,并且不想使用主题提供的样式(虽然是比较奇怪的需求,哈哈),hugo 也是可以支持的,但是需要自己修改一点东西。

经过上面的描述,我们已经知道,主题结构的核心文件其实是 baseof.html 和 single.html 这个文件, 按照约定,baseof.html 这个模板定义了页面的