
Hugo 模板类型完全指南Base、Single、List、Partial、Render Hook 与 Shortcode 的实战与源码解析【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo本篇技术指南以 Hugo 官方文档《Template types》为骨架系统讲解 Hugo 模板体系的全部类型——从作为页面骨架的 Base 模板到渲染具体内容的 Page/Single、列表类模板 Section/List/Taxonomy/Term再到组件化的 Partial、View 模板以及覆盖 Markdown 转换过程的 Render Hook 和从内容页调用的 Shortcode。结合当前 Hugo 仓库的源码如 tpl/tplimpl/templatestore.go、hugolib/template_test.go与文档站自身的模板布局docs/layouts你将掌握每种模板类型的作用、命名规范、回退机制与底层查找原理能够为任意页面创建最精确匹配的模板。模板的存放结构与项目布局所有模板都创建在项目根目录的layouts目录下。虽然一个站点不一定需要用到下面每一种模板但下面这个例子是中等复杂度站点的典型结构来自官方文档《Template types》layouts/ ├── _markup/ │ ├── render-image.html -- render hook │ └── render-link.html -- render hook ├── _partials/ │ ├── footer.html │ └── header.html ├── _shortcodes/ │ ├── audio.html │ └── video.html ├── books/ │ ├── page.html │ └── section.html ├── films/ │ ├── _views/ │ │ └── card.html -- view template │ ├── page.html │ └── section.html ├── baseof.html ├── home.html ├── page.html ├── section.html ├── taxonomy.html └── term.html几点值得注意的约定以下划线开头的_markup/、_partials/、_shortcodes/是三类特殊目录分别存放渲染钩子、局部模板和短代码模板_views/子目录用于存放与某内容类型绑定的视图模板以内容类型命名的顶层目录如books/、films/存放该内容类型专属的page.html与section.htmlbaseof.html、home.html等通用模板位于layouts根目录。Hugo 通过模板查找顺序template lookup order来决定每个页面使用哪个模板文件。文档特别强调创建模板前必须透彻理解模板查找顺序因为模板的选择依据是模板类型、页面 Kind、内容类型、section、语言和输出格式的组合。[!NOTE] 模板可以同时存在于项目自身的layouts目录和主题的layouts目录中Hugo 会选择最具体的那一个并在项目中与主题间交错查找。Base 模板全站布局的基石Base模板作为其他模板可以构建于其上的基础布局通常定义 HTML 的公共结构元素如html、head、body并包含跨页面反复出现的页眉、页脚、导航和脚本引入。把这些公共部分在Base模板中统一定义一次可以避免冗余、保证一致性并简化站点维护。Hugo 可以对以下模板类型应用Base模板home、page、section、taxonomy、term、single、list 和 all。当解析这些模板类型时只有同时满足以下两个条件才会应用Base模板模板中必须至少包含一个defineaction模板中只能包含defineaction、空白和模板注释不允许有任何其他内容。[!NOTE] 如果模板不满足上述全部条件Hugo 会原样执行该模板不会应用Base模板。应用Base模板时Hugo 会用被应用模板中对应defineaction 的内容替换Base模板中的blockaction。下面这个Base模板通过partial函数引入head、header和footer元素blockaction 作为占位符其内容会被匹配的defineaction 替换!DOCTYPE html html lang{{ site.Language.Locale }} dir{{ or site.Language.Direction ltr }} head {{ partial head.html . }} /head body header {{ partial header.html . }} /header main {{ block main . }} This will be replaced with content from the corresponding define action found in the template to which this _base_ template is applied. {{ end }} /main footer {{ partial footer.html . }} /footer /body /html{{ define main }} This will replace the content of the block action found in the _base_ template. {{ end }}源码视角Base 模板的查找与历史兼容当前 Hugo 仓库对 Base 模板的处理在模板存储阶段完成。tpl/tplimpl/templatestore.go 中的fromLegacyPath函数体现了新模板系统对旧版本命名规则的兼容在 Hugo 0.146.0 之前baseof 关键字前会先拼接一个标识符layout、type 或 kind并用连字符分隔例如/docs/list-baseof.html新系统会把这个标识符移动到 baseof 关键字之后并去掉连字符转换为/docs/baseof.list.html的形式。这意味着旧项目中的list-baseof.html类命名在现代 Hugo 中依然有效。在 hugolib/template_test.go 的TestTemplateBOM测试中可以看到 Base 模板与普通模板配合的完整链路baseof.html中定义{{ block main . }}base main{{ end }}single.html中定义{{ define main }}Hi!?{{ end }}最终生成页面断言Base: Hi!?验证了 block 被 define 替换的机制。TestTemplateManyBaseTemplateshugolib/template_test.go进一步验证了为 100 个页面分别生成专属 baseof 模板的场景。当前文档站自身的 docs/layouts/baseof.html 就是一个真实的大规模 Base 模板示例它通过partial引入了导航、搜索、面包屑等大量公共组件。Home 模板渲染站点首页Home模板负责渲染站点首页。下面的Home模板先渲染页面内容.Content再遍历站点所有常规页面并输出标题链接。Hugo 会先对它应用Base模板{{ define main }} {{ .Content }} {{ range .Site.RegularPages }} h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ end }} {{ end }}首页列表的过滤、排序和分组可以借助 Hugo 的页面集合方法与函数完成相关速查可参考文档站的 quick-reference/page-collections 目录下关于页面集合的说明。文档站的 docs/layouts/home.html 是生产级示例它组合了特征展示、开源项目介绍、赞助商等大量区块。Page 模板渲染常规页面Page模板渲染一篇常规内容页面。下面的Page模板渲染页面标题和正文内容同样会先应用Base模板{{ define main }} h1{{ .Title }}/h1 {{ .Content }} {{ end }}需要说明的是位于content根目录下的文件其内容类型为page。而content目录下每个子目录会形成一个 sectionsection 内的页面可以拥有针对该 section 的专属模板参见上文layouts/books/page.html的用法。Section 模板渲染栏目列表Section模板渲染某个 section 内的页面列表。下面的Section模板渲染页面标题、正文内容以及当前 section 内的页面列表{{ define main }} h1{{ .Title }}/h1 {{ .Content }} {{ range .Pages }} h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ end }} {{ end }}对列表的过滤、排序和分组同样可以参考页面集合速查表。Taxonomy 模板渲染分类法术语列表Taxonomy模板渲染某个 taxonomy 中的术语term列表。下面的Taxonomy模板渲染页面标题、正文内容和当前分类法下的术语列表{{ define main }} h1{{ .Title }}/h1 {{ .Content }} {{ range .Pages }} h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ end }} {{ end }}Data 对象与术语排序在Taxonomy模板中Data对象提供以下分类法专属方法Singular单数名如 tagPlural复数名如 tagsTermsTerms方法返回一个 taxonomy 对象可以继续调用其任何方法包括Alphabetical按字母排序和ByCount按关联页面数排序。例如用ByCount按每个术语关联的页面数量排序输出术语列表{{ define main }} h1{{ .Title }}/h1 {{ .Content }} {{ range .Data.Terms.ByCount }} h2a href{{ .Page.RelPermalink }}{{ .Page.LinkTitle }}/a ({{ .Count }})/h2 {{ end }} {{ end }}注意这里遍历的元素是“术语 计数”的结构因此通过.Page访问术语页面对象通过.Count获取关联页面数量。文档站的分类法相关测试可在 hugolib/taxonomy_test.go 中查看。Term 模板渲染术语关联页面Term模板渲染与某个 term 关联的页面列表。下面的Term模板渲染页面标题、正文内容和与当前术语关联的页面列表{{ define main }} h1{{ .Title }}/h1 {{ .Content }} {{ range .Pages }} h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ end }} {{ end }}与Taxonomy模板类似在Term模板中Data对象提供以下术语专属方法SingularPluralTermSingle 模板Page 模板的回退Single模板是Page模板的回退方案。如果page模板不存在Hugo 会转而查找single模板。{{ define main }} h1{{ .Title }}/h1 {{ .Content }} {{ end }}在传统的 Hugo 模板体系中layouts/_default/single.html是最常见的回退位置。这一点在文档站 docs/content/en/templates/lookup-order.md 中有详细描述对于常规内容页single pageHugo 会在_default/single.html中查找 HTML 模板对于列表页section 列表、首页、分类法列表、分类法术语则在_default/list.html中查找。List 模板列表类模板的回退List模板是 home、section、taxonomy、term 四种模板的回退方案。若其中某个模板类型不存在Hugo 会转而查找list模板。{{ define main }} h1{{ .Title }}/h1 {{ .Content }} {{ range .Pages }} h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ end }} {{ end }}在传统模板体系中它对应layouts/_default/list.html。文档站的 docs/layouts/list.html 就是这一回退模板的真实落地实现同时它还提供了 docs/layouts/list.rss.xml 作为 RSS 输出格式的列表模板。All 模板终极通用回退All模板是 home、page、section、taxonomy、term、single、list 全部模板类型的回退方案。若其中任一模板不存在Hugo 都会转而查找all模板。下面的All模板按页面 Kind 条件渲染不同类型的内容{{ define main }} {{ if eq .Kind home }} {{ .Content }} {{ range .Site.RegularPages }} h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ end }} {{ else if eq .Kind page }} h1{{ .Title }}/h1 {{ .Content }} {{ else if in (slice section taxonomy term) .Kind }} h1{{ .Title }}/h1 {{ .Content }} {{ range .Pages }} h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ end }} {{ else }} {{ errorf Unsupported page kind: %s .Kind }} {{ end }} {{ end }}这个示例演示了三种典型的 Kind 分支处理home渲染首页内容加常规页面列表page渲染单页标题与内容section/taxonomy/term渲染标题、内容与页面列表其余情况如404通过errorf抛出明确的构建错误帮助开发者尽早发现问题。errorf是 Hugo 模板内置函数在 tpl/fmt 包中有对应实现。Partial 模板可复用组件Partial模板通常用于渲染站点的某个组件但也可以创建返回值的Partial模板。例如下面的Partial模板渲染版权信息pCopyright {{ now.Year }}. All rights reserved./p通过调用partial或partialCached函数执行Partial模板可选地传入上下文作为第二个参数{{ partial footer.html . }}文档站的 docs/layouts/_partials/footer.html 即为此类模板的真实实现而 docs/layouts/_partials/header.html 则展示了如何聚合导航、主题切换、搜索入口等复杂组件。Partial 的命名匹配逻辑与其它模板类型不同Hugo 在查找匹配的Partial模板时不会考虑当前页面的 Kind、内容类型、逻辑路径、语言或输出格式。但它确实会应用与其它模板类型相同的名称匹配逻辑先尝试最具体的匹配找不到再逐步回退到更通用的版本。例如对于如下调用{{ partial footer.section.de.html . }}Hugo 使用如下查找顺序寻找匹配模板layouts/_partials/footer.section.de.htmllayouts/_partials/footer.section.htmllayouts/_partials/footer.de.htmllayouts/_partials/footer.html从源码结构看这种“逐级剥离名称标识符”的匹配逻辑与模板路径解析器common/paths/pathparser.go中对文件名的解析方式相呼应_partials容器目录在 tpl/tplimpl/templatestore.go 中作为containerPartials被特殊识别与处理。内联 Partial 模板Partial模板还可以在其它模板内联定义。需要特别注意的是模板命名空间是全局的必须为这些内联Partial模板保证唯一名称以避免命名冲突。Value: {{ partial my-inline-partial.html . }} {{ define _partials/my-inline-partial.html }} {{ $value : 32 }} {{ return $value }} {{ end }}注意内联模板的define名称以_partials/为前缀这与目录中的命名约定保持一致。文档站的模板转换逻辑中也有对内联模板的显式处理见 tpl/tplimpl/templatetransform.go 的注释。View 模板页面渲染视图View模板与Partial模板类似通过调用Page对象上的Render方法触发。与Partial模板不同的是View模板继承当前页面的上下文可以针对任何页面 Kind、内容类型、逻辑路径、语言或输出格式可以位于layouts目录下的任何层级。例如下面的Home模板渲染页面内容并为站点filmssection 中的每个页面渲染一个卡片组件{{ define main }} {{ .Content }} ul {{ range where site.RegularPages Section films }} {{ .Render _views/card }} {{ end }} /ul {{ end }}div classcard h2a href{{ .RelPermalink }}{{ .LinkTitle }}/a/h2 {{ .Summary }} /divRender方法接受视图模板的路径不含扩展名视图模板内部可以直接使用当前页面的上下文如.RelPermalink、.LinkTitle、.Summary。关于View模板的命名和组织方式请参阅Render方法文档。Render Hook 模板接管 Markdown 到 HTML 的转换Render Hook模板用于覆盖 Markdown 转换为 HTML 的过程。Hugo 内置的渲染器如 Goldmark负责 Markdown 语法解析而渲染钩子允许你在特定元素链接、图片、标题、代码块等输出 HTML 时插入自定义逻辑。例如下面的Render Hook模板在每个标题右侧添加一个锚点链接h{{ .Level }} id{{ .Anchor }} {{- with .Attributes.class }} class{{ . }} {{- end }} {{ .Text }} a href#{{ .Anchor }}#/a /h{{ .Level }}该模板使用.Level标题级别、.Anchor生成的锚点 ID、.Text标题文本和.Attributes附加属性等上下文字段。当前文档站的 docs/layouts/_markup 目录下就包含了render-blockquote.html、render-codeblock.html、render-link.html、render-table.html、render-passthrough.html等多个生产级渲染钩子可作深入学习参考。更多细节参见渲染钩子模板文档。Shortcode 模板从内容页调用Shortcode模板用于渲染站点组件。与Partial、View模板不同Shortcode模板是从内容页面内部调用的。例如下面的Shortcode模板从全局资源中获取音频文件并渲染 audio 元素{{ with resources.Get (.Get src) }} audio controls preloadauto src{{ .RelPermalink }}/audio {{ end }}然后在 Markdown 内容中调用该短代码{{/* audio src/audio/test.mp3 */}}resources.Get用于从站点全局资源中按路径获取资源实现在 resources 模块.Get src读取短代码调用时的参数。文档站的 docs/layouts/_shortcodes 目录包含 20 余个生产级短代码例如img.html、code-toggle.html、include.html等覆盖了从图片处理到文档包含的各类场景。更多细节参见短代码模板文档。其它专用模板除上述模板类型外Hugo 还提供以下专用模板用于生成站点基础设施类输出Sitemaps站点地图RSS feeds订阅源404 错误页面robots.txt 文件这些模板同样遵循模板查找顺序并且可以按输出格式如 XML区分。文档站自带的 docs/layouts/404.html、docs/layouts/list.rss.xml 以及 docs/layouts/home.redir、docs/layouts/home.headers 展示了专用输出重定向规则、响应头的实际用法。结合查找顺序设计你的模板体系用前端属性定向模板你无法改变查找顺序去适配某个内容页但可以通过修改内容页去适配模板在前置元数据front matter中指定type、layout或两者。考虑如下内容结构content/ ├── about.md └── contact.md位于content根目录的文件其内容类型为page。若要让这些页面使用专属模板可创建对应子目录layouts/ └── page/ └── single.html然而 contact 页面通常包含表单需要不同的模板。在前置元数据中指定layouttitle Contact layout contact然后为 contact 页面创建专属模板layouts/ └── page/ ├── contact.html -- 渲染 contact.md └── single.html -- 渲染 about.md作为内容类型page这个词较为模糊也许miscellaneous更贴切。为每个页面添加type# content/about.md title About type miscellaneous# content/contact.md title Contact type miscellaneous layout contact然后把模板放入对应目录layouts/ └── miscellaneous/ ├── contact.html -- 渲染 contact.md └── single.html -- 渲染 about.md影响模板选择的参数根据 docs/content/en/templates/lookup-order.mdHugo 为给定页面选择模板时会考虑以下参数按特异性从高到低排列Kind页面 Kind首页也是一种它同时决定页面是单页single page查找_default/single.html还是列表页list page查找_default/list.htmlLayout可在前置元数据中设置输出格式每个输出格式既有name如rss、amp、html也有suffix如xml、html。Hugo 优先匹配两者都命中的模板如index.amp.html再逐步查找更不具体的模板若输出格式的 Media Type 定义了多个后缀只考虑第一个语言模板名中会考虑语言标签。若站点语言为frindex.fr.amp.html优先于index.amp.html但index.amp.html又优先于index.fr.htmlType取前置元数据中type的值未设置时取根 section 名如 blog它永远有值未设置则为 pageSection对section、taxonomy和term类型相关。小结模板类型全景模板类型渲染对象回退关系典型文件Base公共布局骨架无作为其它模板的外壳layouts/baseof.htmlHome站点首页可回退到 List、Alllayouts/home.htmlPage常规内容页可回退到 Single、Alllayouts/page.htmlSection栏目列表页可回退到 List、Alllayouts/section.htmlTaxonomy分类法术语列表可回退到 List、Alllayouts/taxonomy.htmlTerm术语关联页面列表可回退到 List、Alllayouts/term.htmlSinglePage 的回退可回退到 Alllayouts/single.htmlList列表类模板的回退可回退到 Alllayouts/list.htmlAll所有模板的终极回退无layouts/all.htmlPartial可复用组件 / 返回值名称逐级回退匹配layouts/_partials/*.htmlView页面渲染视图无layouts/**/_views/*.htmlRender Hook覆盖 Markdown→HTML 转换无layouts/_markup/*.htmlShortcode从内容页调用的组件无layouts/_shortcodes/*.html合理利用这套模板类型体系配合模板查找顺序的精确匹配规则你既可以用 Base Partial 构建高度一致的页面骨架又可以通过内容类型目录、视图模板和渲染钩子为任意页面形态提供专属渲染方案——这正是 Hugo 灵活模板系统的核心价值所在。【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考