news 2026/9/13 18:44:26

Zola 分类法(Taxonomies)模板开发指南:从配置、术语渲染到分页与 Feed 的完整实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Zola 分类法(Taxonomies)模板开发指南:从配置、术语渲染到分页与 Feed 的完整实战

Zola 分类法(Taxonomies)模板开发指南:从配置、术语渲染到分页与 Feed 的完整实战

【免费下载链接】zolaA fast static site generator in a single binary with everything built-in. https://www.getzola.org项目地址: https://gitcode.com/GitHub_Trending/zo/zola

Zola 内置的 Taxonomies(分类法)机制允许你按自定义维度对内容分组,并在构建期为每个分类与术语生成独立的列表页、术语页和订阅 Feed。本文将基于 Zola 官方模板文档,结合仓库源码与测试站点,完整讲解分类法模板的文件查找规则、TaxonomyConfigTaxonomyTerm的数据结构、list.htmlsingle.html的变量体系,以及分页(paginator)与 Feed 的集成方式,让你能直接写出可运行、可复用的分类模板。

分类法模板的作用与适用前提

在 Zola 中,分类法(Taxonomy)是用户自定义的分类维度,术语(Term)是某个维度下的具体分组,而值(Value)则是被关联到术语的内容条目。构建时,Zola 会为每个分类生成两类页面:

  • 分类列表页:列出该分类下的所有术语,例如/tags/
  • 术语页:列出归属于某个术语的所有页面,例如/tags/rust/

模板文档明确指出:只有在至少一个分类法设置了render = true时,分类法模板才是必需的。也就是说,如果某个分类只用于聚合内容而不需要生成页面(例如仅用于 Feed 或内部查询),可以关闭渲染;反之,只要有一个分类需要页面输出,就必须提供对应的模板(或使用内置回退模板)。

模板文件的查找规则:专用目录与通用回退

Zola 会先在templates目录下按分类法名称查找专用模板:

  • $TAXONOMY_NAME/single.html(分类法名称为子目录)
  • $TAXONOMY_NAME/list.html

如果找不到,则回退到通用模板:

  • taxonomy_single.html
  • taxonomy_list.html

这一点在仓库的渲染实现中有直接印证:components/render/src/renderer.rs中,渲染术语页时使用cached.single_template.as_deref().unwrap_or("taxonomy_single.html"),渲染列表页时使用cached.list_template.as_deref().unwrap_or("taxonomy_list.html"),即优先采用按分类法缓存的模板名,缺失时回退到通用模板名。

在官方文档站点docs/中就能看到实际用法:docs/config.toml定义了taxonomies = [{ name = "theme-tags" }],对应的专用模板放在docs/templates/theme-tags/目录下(single.htmllist.html),按分类法名组织模板目录。

注意:模板文件必须放在站点根目录的templates/下;主题(theme)的模板也遵循同样的查找顺序。

模板可用的数据类型

在编写模板之前,需要先理解 Zola 暴露给模板的两个核心类型:TaxonomyTermTaxonomyConfig

TaxonomyTerm:单个术语对象

name: String; // 术语名称(原始写法,如 "Guillermo Del Toro") slug: String; // 术语的 slug(用于 URL,如 "guillermo-del-toro") path: String; // 术语页的路径 permalink: String; // 术语页的完整永久链接 pages: Array<Page>; // 归属于该术语的页面数组 page_count: Number; // 归属于该术语的页面数量

从源码components/content/src/taxonomies.rs可以确认,TaxonomyTerm内部字段即为nameslugpathpermalinkpages,序列化后额外暴露page_count(等于item.pages.len())。术语页面的排序由sort_pages(taxo_pages, SortBy::Date)按日期完成,无日期的页面会追加到末尾——这符合分类法"几乎总是用于博客"的定位。

TaxonomyConfig:分类法的配置对象

name: String; // 分类法名称(通常用复数,如 tags) paginate_by: Number?; // 若为正数,每个术语页按此数量分页 paginate_path: String?;// 分页路径,默认 "page",页码追加其后 feed: Bool; // 是否为每个术语生成 Feed(默认 false) render: Bool; // 是否渲染分类与术语页面(默认 true)

源码components/config/src/config/taxonomies.rsTaxonomyConfig的默认值为:paginate_by: Nonerender: truefeed: falsepaginate_path未设置时默认取"page"(即默认分页链接形如/tags/rust/page/1)。同时is_paginated()要求paginate_by必须是大于 0 的数才真正启用分页。

分类列表模板(list.html)

list.html渲染分类法下的所有术语,该模板永远不会被分页,因此在所有情况下都获得以下变量:

config: Config; // 站点配置 taxonomy: TaxonomyConfig; // 该分类法的配置数据 current_url: String; // 当前页面的完整永久链接 current_path: String; // 当前页面的路径 terms: Array<TaxonomyTerm>;// 该分类法下的所有术语 lang: String; // 当前页面语言

一个典型的列表模板会遍历terms,输出每个术语的名称、数量与链接:

<ul> {% for term in terms %} <li> <a href="{{ term.permalink | safe }}">{{ term.name }}</a> ({{ term.page_count }}) </li> {% endfor %} </ul>

仓库测试站点test_site/templates/tags/list.html提供了一个更精简的真实示例:

{% for tag in terms %} {{ tag.name }} {{ tag.slug }} {{ tag.pages | length }} {% endfor %}

可以看到terms中每个元素都具备nameslugpages等字段,与文档声明的TaxonomyTerm结构一致。

单个术语模板(single.html)

single.html渲染某个具体术语下的所有页面,获得以下变量:

config: Config; // 站点配置 taxonomy: TaxonomyConfig; // 该分类法的配置数据 current_url: String; // 当前页面的完整永久链接 current_path: String; // 当前页面的路径 term: TaxonomyTerm; // 当前正在渲染的术语 lang: String; // 当前页面语言

如果该术语启用了分页(paginate_by为正数),模板还会额外获得一个paginator变量,其结构与分区(section)分页完全一致,详见 分页模板文档。

测试站点test_site/templates/tags/single.html展示了同时兼容分页与非分页的写法:

{% if not paginator %} Tag: {{ term.name }} {% for page in term.pages %} <article> <h3 class="post__title"><a href="{{ page.permalink | safe }}">{{ page.title | safe }}</a></h3> </article> {% endfor %} {% else %} Tag: {{ term.name }} {% for page in paginator.pages %} {{ page.title | safe }} {% endfor %} Num pagers: {{ paginator.number_pagers }} Page size: {{ paginator.paginate_by }} Current index: {{ paginator.current_index }} {% if paginator.previous %}has_prev{% endif %} {% if paginator.next %}has_next{% endif %} {% endif %}

关键点在于:未分页时页面在term.pages中,分页后页面在paginator.pages,模板必须用{% if not paginator %}分流处理。

官方文档站点自己的docs/templates/theme-tags/single.html是术语页的真实生产案例:它遍历term.pages,为每个主题生成卡片链接:

<h1>Zola themes in {{ term.name }}</h1> <div class="themes"> {% for theme in term.pages %} <a class="theme" href="{{ theme.permalink }}"> <img src="{{ theme.permalink }}screenshot.png" alt="Screenshot of {{ theme.title }}"> <span>{{ theme.title }}</span> </a> {% endfor %} </div>

这展示了术语页的典型用途:term.name作为页面标题,term.pages作为内容列表。

分页变量(paginator)

分页术语页得到的paginator变量类型为Pager,核心字段如下:

paginate_by: Number; // 每页条目数 base_url: String; // 分页基础 URL,可拼接整数得到任意页码链接 number_pagers: Number; // 分页总数 first: String; // 第一页链接 last: String; // 最后一页链接 previous: String?; // 上一页链接(若有) next: String?; // 下一页链接(若有) pages: Array<Page>; // 当前页的所有页面 current_index: Number; // 当前页码(从 1 开始) total_pages: Number; // 全部分页中的页面总数

文档明确提醒:paginate_by未设置为正数时,paginator变量不会被定义,因此模板中必须用{% if paginator %}{% if not paginator %}进行判空。分页链接的经典写法如下:

<nav class="pagination"> {% if paginator.previous %} <a class="previous" href="{{ paginator.previous }}">‹ Previous</a> {% endif %} {% if paginator.next %} <a class="next" href="{{ paginator.next }}">Next ›</a> {% endif %} </nav>

如果需要给每个分页生成链接,可以借助paginator.base_urlpaginator.number_pagers拼接:

{% for i in range(end=paginator.number_pagers) %} <a href="{{ paginator.base_url }}{{ i + 1 }}">{{ i + 1 }}</a> {% endfor %}

从配置到输出的完整工作流

1. 在配置文件中声明分类法

分类法必须声明在zola.toml主 section(即[extra]之外)中,例如:

taxonomies = [ { name = "director", feed = true }, { name = "genres", feed = true }, { name = "awards", feed = true }, { name = "release-year", feed = true }, ]

多语言站点需要同时在对应语言 section 下重复声明:

taxonomies = [ { name = "director", feed = true }, { name = "genres", feed = true }, ] [languages.fr] taxonomies = [ { name = "director", feed = true }, { name = "genres", feed = true }, ]

这里feed = true表示每个术语都会生成 Atom Feed(默认格式)。

2. 在页面 front matter 中标记术语

配置完成后,在内容页的 front matter 中通过[taxonomies]指定归属:

+++ title = "Shape of water" date = 2019-08-15 [taxonomies] director = ["Guillermo Del Toro"] genres = ["Thriller", "Drama"] awards = ["Golden Globe", "Academy award", "BAFTA"] release-year = ["2017"] +++

3. 提供模板并构建

创建templates/tags/list.htmltemplates/tags/single.html(或通用回退模板taxonomy_list.html/taxonomy_single.html),然后运行zola build即可。仓库渲染器会依次调用render_taxonomy_listrender_taxonomy_term(见components/render/src/renderer.rs),分别注入terms/termtaxonomyconfiglangcurrent_urlcurrent_path等变量。

输出路径与大小写合并规则

分类法页面的输出路径遵循以下规则:

$BASE_URL/$NAME/ (分类列表页) $BASE_URL/$NAME/$SLUG (术语页)
  • 分类法名称从不进行 slugify,URL 中直接使用配置里的name
  • 术语会进行 slugify(当配置slugify.taxonomies = "on"时,这是默认值),见 配置文档。

若设置了taxonomy_root配置项,则所有分类路径都会加上该前缀:

$BASE_URL/$TAXONOMY_ROOT/$NAME/ (分类列表页) $BASE_URL/$TAXONOMY_ROOT/$NAME/$SLUG (术语页)

例如taxonomy_root = "blog"、分类tags、术语rust时:

  • 分类列表页:$BASE_URL/blog/tags/
  • 术语页:$BASE_URL/blog/tags/rust/

该行为在源码测试components/content/src/taxonomies.rs中被直接验证:taxonomy_path_with_taxonomy_root断言tax.path == "/blog/tags/"term.path == "/blog/tags/rust/",而未设置taxonomy_root时路径为/tags//tags/rust/

另一个重要规则是分类法不区分大小写:slug 相同的术语会被合并。测试merges_terms_with_different_case验证了"League of legends""League of Legends"两个术语最终合并为唯一一项(slug 为league-of-legends),且两个页面都被保留。此外,如果术语 slugify 后为空字符串(例如术语仅含;这类特殊字符),构建会直接报错(对应测试taxonomy_slug_is_empty_errors),因此术语命名应避免纯特殊字符。

Feed 与 SEO 最佳实践

当分类配置了feed = true时,Zola 会为每个术语生成 Atom Feed(例如/tags/rust/atom.xml),订阅者可以只关注特定分类下的内容更新。渲染器中的render_taxonomy_feed(见components/render/src/renderer.rs)负责这一输出,模板层无需额外处理。

关于 SEO,官方分页文档特别建议:不要将分页页面纳入 sitemap,因为分页页是非 canonical 页面。在zola.toml中设置:

exclude_paginated_pages_in_sitemap = "all"

即可将全部分页页面从 sitemap 中排除。

小结

Zola 的分类法模板体系可以总结为:一个配置声明、两组模板文件、两类渲染上下文

  • 配置层:在主 section 声明taxonomies,每个分类可设置namepaginate_bypaginate_pathfeedrenderlang
  • 模板层:按分类名组织$TAXONOMY_NAME/{list,single}.html,或使用通用回退模板taxonomy_{list,single}.html
  • 数据层list.html拿到全部termssingle.html拿到当前term,启用分页时额外获得paginator

掌握这些规则后,你既能写出官网主题目录(theme-tags)那样的术语聚合页,也能为博客搭建带分页、带 Feed 的标签系统。更深入的模板变量细节,可继续阅读 模板概览、页面与分区模板 与 分页模板 等相关文档。

【免费下载链接】zolaA fast static site generator in a single binary with everything built-in. https://www.getzola.org项目地址: https://gitcode.com/GitHub_Trending/zo/zola

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 18:41:13

猫抓 cat-catch:3 分钟跑通第一次网页视频下载,M3U8 解密也不难

猫抓 cat-catch&#xff1a;3 分钟跑通第一次网页视频下载&#xff0c;M3U8 解密也不难 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 想存网页上…

作者头像 李华
网站建设 2026/9/13 18:38:45

基于YOLOv3+PyQt5的交通路口智能监控系统实现与部署

简介&#xff1a;一套基于YOLOv3目标检测与PyQt5图形界面开发的交通路口智能监控系统完整源码包&#xff0c;面向计算机视觉开发者、Python后端工程师及智能交通方向学习者&#xff0c;提供从流媒体接入、目标检测到客户端展示的端到端技术方案。压缩包共113个文件&#xff08;…

作者头像 李华
网站建设 2026/9/13 18:36:19

YASA自动化多导睡眠图分析:从EDF到睡眠分期与纺锤波检测

简介&#xff1a;这是一份面向睡眠研究人员、脑电数据分析者及Python开发者的YASA工具箱完整源码包。YASA专注于多导睡眠图&#xff08;PSG&#xff09;的自动分析&#xff0c;涵盖自动睡眠分期、纺锤波/慢波/快速眼动事件检测、伪影剔除、频谱分析及催眠图统计等功能&#xff…

作者头像 李华
网站建设 2026/9/13 18:35:56

LunaTranslator 使用指南:GalGame 翻译工具三种取词模式的完整流程

LunaTranslator 使用指南&#xff1a;GalGame 翻译工具三种取词模式的完整流程 【免费下载链接】LunaTranslator 视觉小说翻译器 / Visual Novel Translator 项目地址: https://gitcode.com/GitHub_Trending/lu/LunaTranslator 如果你在玩日文或英文游戏&#xff0c;想按…

作者头像 李华
网站建设 2026/9/13 18:35:45

大数据时代的数据分片技术原理与实践指南

1. 数据分片技术概述 在大数据时代&#xff0c;数据量呈指数级增长&#xff0c;传统单一数据库架构已无法满足海量数据存储和高并发访问的需求。数据分片&#xff08;Sharding&#xff09;作为一种有效的分布式数据管理技术&#xff0c;通过将数据分散存储在多个数据库节点上&a…

作者头像 李华