zola 是一个静态网站生成器(static site generator, SSG),与 Hugo,Jekyll 等类似。
SSG 的功能是,使用模板引擎,将原始内容转换为静态 HTML 网页。SSG 适合搭建博客、知识库、落地页等。所谓静态网站,其所有的页面都是事先生成好的,而不是在用户访问时当场生成(当场生成即为「动态」)。我们日常访问的大部分网站是动态网站,它们最明显的特征是,多次访问同一个 url 时,用户看到的内容可以是不同的。
zola 支持用 CommonMark 撰写内容,并使用 Tera 模板引擎将网站内容渲染为 HTML。CommonMark 是一个严格定义的 Markdown 规范。
zola 是用 Rust 语言实现的,代码仓库在 https://github.com/getzola/zola ,20000+ 行代码,规模不算大。zola 使用 pulldown-cmark 解析 Markdown 文件。Tera 模板引擎的语法,与流行的 Jinja2 相似。
Quick Start
zola 是一个命令行工具,我们可以根据官方文档 Installation页面的提示,在各个操作系统上安装 zola。
这里我们通过 zola 搭建一个博客,展示 zola 的基本用法。
首先,我们需要初始化博客项目。
$ zola init yfamingblog
zola 会询问如下问题:
> What is the URL of your site? (https://example.com):
> Do you want to enable Sass compilation? [Y/n]:
> Do you want to enable syntax highlighting? [y/N]:
> Do you want to build a search index of the content? [y/N]:
根据你的需求回答即可。如果不明白,也可以直接按回车,zola 会选择默认答案。不用担心,以后可以在配置文件里修改。
执行完毕,我们发现 zola 已经创建了 yfamingblog
目录,这就是我们博客网站的项目目录了。
$ cd yfamingblog
$ tree
.
├── config.toml # 配置文件
├── content # 内容,里面放 markdown 文件
├── sass # sass
├── static # 静态资源 CSS,Javascript,图片什么的,都可以放这里
├── templates # 模板
└── themes # 主题
打开检查一下,config.toml
里的内容,正是 init
时提供的答案。而 content
, sass
, static
, templates
, themes
均为空目录。
虽然现在博客里什么内容也没有,但它已经是个完整的网站了,我们可以看看效果。
$ zola serve
Building site...
Checking all internal links with anchors.
> Successfully checked 0 internal link(s) with anchors.
-> Creating 0 pages (0 orphan) and 0 sections
Done in 49ms.
Web server is available at http://127.0.0.1:1111 (bound to 127.0.0.1:1111)
Listening for changes in /.../yfamingblog/{config.toml,content,sass,static,templates}
Press Ctrl+C to stop
根据提示,在浏览器里打开 http://127.0.0.1:1111/ ,就可以访问了。
注意截图里的那一行字,"You're seeing this page because we couldn't find a template to render"。看到这个页面,是因为 zola 找不到与这个页面对应的模板文件。如果我们没有为页面(Markdown 文件)设置好对应的模板,它就会被渲染成这样。记住这个页面,看到它就意味着哪里出问题了。
接下来需要做的事情,已经体现在这几个空目录里面了。我们需要给博客填充内容,设置模板和主题等等。
但是,我们不必等这一切完成,现在就可以构建并部署了。
$ zola build
Building site...
Checking all internal links with anchors.
> Successfully checked 0 internal link(s) with anchors.
-> Creating 0 pages (0 orphan) and 0 sections
Done in 19ms.
构建之后,项目里会增加 public
目录,这就是用来保存构建结果的地方。
我们部署网站的时候,需要把这个目录里的内容全部 copy 到服务器。一般来说,我们会通过自动化程序,比如 GitHub Actions 来帮我们完成构建和部署。
除了部署到自己的服务器,我们也可以部署到 GitHub Pages,Cloudflare Pages,Vercel,Netlify 等平台。这些平台都有免费计划,支持绑定自己的域名,支持 HTTPS,而且支持从 GitHub 仓库自动部署。这样的话,运营一个博客就只剩域名成本了,每年 $10 左右就足够。
zola 的命令行非常简洁,至此我们已经使用过 init
,serve
,build
3 个子命令,差不多覆盖了 zola 的全部功能了。
$ zola -h
A fast static site generator with everything built-in
Usage: zola [OPTIONS] <COMMAND>
Commands:
init Create a new Zola project
build Deletes the output directory if there is one and builds the site
serve Serve the site. Rebuild and reload on change automatically
check Try to build the project without rendering it. Checks links
completion Generate shell completion
help Print this message or the help of the given subcommand(s)
Options:
-r, --root <ROOT> Directory to use as root of project [default: .]
-c, --config <CONFIG> Path to a config file other than config.toml in the root of project [default: config.toml]
-h, --help Print help
-V, --version Print version
completion
和 help
都是辅助功能,可忽略。而 check
像 build
一样尝试构建页面,只是不会将结果写入 public
目录。同时 build
还会检查 Markdown 文件的所有外链。
当我们把博客网站初步搭建起来,设置了模板和主题,并且完成了 build 和部署之后,网站就算正式上线了。
在此之后,当我们发布新的文章时,只需要在 content
目录添加一个 Markdown 文件,然后进行 build 和部署操作即可。
博客规划
Quick Start 让我们对 zola 有了初步的体验。但是在正式开始搭建博客前,我们需要规划一下,确定博客需要哪些页面,展示什么内容,使用怎样的风格,支持哪些功能。
好在个人博客非常简单也很成熟,近乎标准化了,以下是我的规划。
博客需要的页面包括:
/
首页,最近的 10 篇博客列表,展示发布时间和标题。/post/xxxx
博客文章页,包括标题、发布时间、tags、正文。或许考虑在右侧顶部固定展示文章大纲,以方便阅读长文。/about
博客的介绍页。/rss
RSS 订阅。/archives
Archive 页。列出文章标题和发布时间,按年份分组。暂时不需要,超过 20 篇再考虑。/tags
暂时不需要,超过 20 篇再考虑。
如果有可能,希望使用「霞鹜文楷」字体。但这个优先级不高,上线之后再折腾吧。
每个页面顶部,需要一个导航条,放上 About
,RSS
链接。以后内容更多了,再考虑在导航条上也加上 Tags
,Archives
链接。
网站采用单栏式布局,保持简洁。考虑到阅读长文时,展示文章大纲(toc)会更方便,在文章页采用双栏式布局也是可以的。
zola 自带了搜索功能,但是考虑到通过 Google 的 site
语法搜索特定网站已经很方便了,因此就不添加搜索了。
评论功能,暂时也不需要。主要原因是初期评论会很少,而且会招来一些 spam 流量,不划算。未来也许会考虑接入一个现成的评论系统。
访问统计功能,直接用 Google Analytics 等常用工具就行,优先级不高。
博客主题将会决定绝大部分的展示效果,挑选一个符合需求的主题,就成了最重要的事情。zola 已经有 100+ 个主题,浏览之后,我决定使用 apollo 主题。
深入了解 zola
接下来,我们一边深入了解 zola,一边完成博客搭建工作,边学边干。
个人博客是非常简单的,但读 zola 文档,时常会发现有些地方比博客复杂多了。此时我们可以想象有一个比较早期的资讯网站,它存在多个频道,每个频道都有自己的文章列表。每个频道都支持单独的 RSS 订阅,首页上还需要对频道排序等等需求。结合这个网站的需求来理解 zola,它的设计就显得合理很多了。
内容 content
,section
与 page
content
目录的结构,决定了 zola 生成的静态网站的结构。content
里的每一个 Markdown 文件生成一个 HTML 文件。而且,content
的子目录和文件的层次结构,也反应在 HTML 的 URL 路径里。
比如,content
目录结构及对应的 HTML 页面如下:
$ tree content
content
├── about.md # <base_url>/about/
└── post
├── _index.md # <base_url>/post/
├── blog_with_zola.md # <base_url>/post/blog-with-zola/
└── hello_world.md # <base_url>/post/hello-world/
└── legacy
└── whatever.md # <base_url>legacy/whatever.md/
content
里的 Markdown 文件,被称为 page
。content
里的子目录,有时候我们希望为它也生成一个 HTML。此时我们需要在这个子目录里添加一个名为 _index.md
的 Markdown 文件,子目录的 HTML 内容由它决定。如果一个子目录里包含 _index.md
我们则称它为 section
。
zola 生成的 HTML 页面,要么是一个 section
,要么是一个 page
。
子目录里可以继续添加子目录,所以 section
也是层次结构的,一个 section
可以有多个子 section
。
_index.md
用于表示 section 了,那么自然不会被视为 page
了。如果子目录里没有 _index.md
那么这个目录就不是 section
,不会为这个子目录生成 HTML 文件。但是,无论子目录是不是 section
,它里面的 Markdown 文件都会作为 page
生成 HTML 文件。
总结一下,一个 section
可以拥有 0 个 1 个或多个 page
,一个 section
可以拥有 0 个 1 个或多个子 section
。但是,一个 page
可以属于某个 section
也可以不属于任何 section
(称为 orphan page
)。
这里的示例中,post/
子目录就是一个 section
,而 legacy/
不是。因此,post/
子目录会生成一个对应的 HTML 页面,而 legacy/
子目录则不会。但是,legacy/wahtever.md
仍然会生成一个 HTML 页面。
zola 在生成 HTML 时,如果当前生成的是 section
,那么就可以在 Tera 模板里使用 section
变量,并且通过 section.subsections
和 section.pages
来访问子 section
和所拥有的 page
。如果当前生成的是 page
,那么就可以在 Tera 模板里使用 page
变量。
值得一提的是,网站首页也被视为一个 section
,无论是否存在 content/_index.md
文件。content
目录里定义的所有 section
均为首页 section
的子 section
,且 content
目录里直接定义的 page
都归首页 section
所有。
我们可以在 Markdown 文件中通过 front matter 给 section
和 page
添加元信息,比如标题、发布日期、作者、tag 等等。
+++
title = "Hello World"
date = 2025-06-30
slug = "hello_world_with_underscore_instead_of_hyphen"
template = "article_v2.html"
+++
front matter 被 Markdown 工具广泛支持。它必须出现在 Markdown 文件的最前面,处在两个 ---
行,或者 +++
行之间。而 front matter 的内容,则要遵守 YAML 或者 TOML 格式。
zola 支持的 front matter 需要放在两个 +++
行之间,并且使用 TOML 格式。(注意,zola 也支持 ---
+ YAML 格式的 front matter 以方便遗留系统迁移。)
section
和 page
支持的 front matter 字段,分别见 Section - Front Matter 和 Page - Front Matter
值得一提的是,section
和 page
都支持在 front matter 中通过 template
字段,来指定生成 HTML 时应当使用的模板文件。
section
还支持通过 page_template
字段来指定下属 page
在生成 HTML 应当使用的模板文件。当然,page
自己通过 template
指定的优先级更高一些。
为了方便 Markdown 之间的相互引用,zola 添加了一个新的链接语法。形如:
[hello world](@/post/hello_world.md)
这样就可以在 Markdown 中添加对 content/post/hello_world.md
的链接了。这里 @/
可视为 content/
,但写起来更简洁。
模板 templates
SSG 的功能用一句话描述,就是 内容(Markdown)+ 模板 ==> HTML 文件
。
在上一节我们了解过内容 content
的工作机制。content
目录的结构,决定了 zola 生成的静态网站的结构。而且 zola 生成的 HTML 页面,要么是 section
,要么是 page
。
这一节我们来了解模板,即 template
目录里的内容,以及,当渲染一个 section
或 page
时,应当选择哪个模板文件。
zola 使用 Tera 作为模板引擎。与所有其他的模板引擎类似,Tera 支持变量 {{ var }}
,分支判断 {% if ... %}...{% endif %}
,循环 {% for e in ... %}...{% endfor %}
,还有 filter,模板继承等常见的功能。
zola 在生成 HTML 时,如果当前生成的是 section
,那么就可以在 Tera 模板里使用 section
变量;如果当前生成的是 page
,那么就可以在 Tera 模板里使用 page
变量。并且,我们可以在模板中用 {{ __tera_context }}
打印出渲染当前页面时能够使用的全部模板变量,开发调试时使用它非常方便。
zola 为 section
和 page
提供了相当多的字段,以便在 Tera 模板中使用,比如 page.permalink
,page.toc
,page.assets
,section.subsections
,section.pages
等等。具体见 Templates / Sections and Pages。有些字段是从 Markdown 文件的 front matter 里解析的,有些则是 zola 自己解析/生成/计算而得到的。
其中 section.subsections
是一个字符串数组,元素为子 section
的 _index.md
相对路径,如 post/_index.md
。将这个字符串传给 get_section
函数(详后),就可以加载子 section
并访问其更详细的信息了。
zola 在为 section
和 page
生成 HTML 页面时,使用什么规则来决定使用哪个模板文件呢?
如果当前渲染的是 page
,则依次:
- 如果 front matter 里通过
template
指定了,就使用指定的模板 - 如果
page
所属section
的_index.md
文件的 front matter 里通过page_template
指定了,就使用这个指定的模板。 - 如果直属
section
没有指定,但直属section
的父section
指定了,就使用这个指定的模板。并且递归向上找到最顶层的section
为止。 - 如果仍然没有指定,则使用
templates
目录里的page.html
模板。 - 如果还是不存在,就会用 zola 内置的默认模板。(如前面截图,页面会展示 "Welcome to Zola!",见 zola 代码
components/utils/src/default_tpl.html
。)
可以看到这个规则是有优先级的。优先由 page
在其 front matter 指定,然后依次取 section
及父 section
及更高层级 section
指定的模板,最后取 templates/page.html
,直到最后使用 zola 内置的默认模板。
如果当前渲染的是 section
,则依次:
- 如果
section
的_index.md
文件的 front matter 里通过template
字段指定了,就使用指定的模板。(首页section
可以没有_index.md
文件。) - 如果没有指定,就使用
templates/index.html
(首页section
)或templates/section.html
(非首页section
)为模板。 - 如果模板文件仍不存在,就使用 zola 内置的默认模板(带有 "Welcome to Zola!" 字样)
总结一下,templates/index.html
,templates/section.html
和 templates/page.html
是 zola 的「标准模板」,我们可以自定义这几个模板的内容,来达成渲染效果。同时,我们可以在 front matter 里指定当前 section
/page
使用的模板,这样的优先级更高。
zola 添加了一些函数,我们可以在模板里直接使用,并用来开发一些复杂的功能。比如:
get_page(path)
,获取某个 Markdown 文件对应的page
。get_section(path)
,获取某个_index.md
文件对应的section
。get_url(path)
,获取某个 path 的 permalink。
这几个函数都需要使用 path 参数。在 zola 中,如何根据 path 查找对应的文件,其逻辑见 File searching logic
{% set page = get_page(path="post/page2.md") %}
{% set section = get_section(path="post/_index.md") %}
{% set url = get_url(path="@/blog/_index.md") %}
{% set url = get_url(path="rss.xml") %}
{% set url = get_url(path="sitemap.xml") %}
前面提到,content
目录的结构决定了生成的静态网站的结构,我们也应当按照这个规则来组织内容。
然而,某些时候,这个规则难以满足我们的需求。比如,我们要为博客设置一个 /archives
页面,并在这个页面按时间顺序展示文章列表。然而很明显,博客文章有自己的 section
(比如 content/post/_index.md
),与 /archives
页面是毫无关系的。
此时,我们可以添加一个 page
即 content/archives.md
,并在 front matter 里通过 template
指定使用 templates/archives.html
模板。而在 templates/archives.html
里,我们可以通过 get_section(path="post/_index.md")
来加载文章所属的 section
,并且通过 section.pages
来访问所有的博客文章。
另外,我们可以通过 {{ __tera_context }}
查看当前模板可以使用的模板变量有哪些。
feed, sitemap 与 robots.txt
feed 订阅,sitemap 以及 robots.txt 等也是网站常用的页面,zola 提供了内置支持,只需要在 config.toml
里配置即可,不需要自己配置 section
/page
和模板来现实。
对于 Feed 我们需要在 config.toml
添加以下配置:
generate_feeds = true
feed_filenames = ["rss.xml"]
对于 feed_filenames
zola 内置支持 atom.xml
和 rss.xml
,分别生成 Atom 1.0 和 RSS 2.0 格式的订阅页面。生成的页面 url 分别是 <base_url>/atom.xml
和 <base_url>/rss.xml
。如果 feed_filenames
添加别的值,就得自己提供模板来生成页面了。
feed_filenames
默认为 atom.xml
,因此 config.toml
里不添加此配置项也是 OK 的。
需要注意的是,只有 page
设置了 date
才会出现在订阅中,可通过 front matter 设置。
而订阅里文章的 author
字段,按照以下规则确定:
- front matter 里
authors
的第一位 - 如果没有,则取
config.toml
里的author
- 仍然没有,则为 "Unknown"
在 section
的 front matter 里设置 generate_feeds = true
则可为 section
生成 feed。
zola 会为网站自动生成 sitemap 和 robots.txt,不需要配置。url 分别是 <base_url>/sitemap.xml
和 <base_url>/robots.txt
。
taxonomies 与 tags, categories
Zola 内置支持 taxonomy(分类法),taxonomy 是用户根据用户定义的类别对内容进行分组的一种方式。通过 taxonomy 可以实现博客常见的 tags 和 categories。
taxonomy 理解起来似乎有点绕,但 Content / Taxonomies 文档里电影网站的示例非常形象,值得一看。
taxonomy 的定义包括 3 部分:
- taxonomy,即分类法的名字。如果我们要将电影按照 director,genres,awards,release year 进行归类,那么 director,genres,awards,release year 各自就是一个 taxonomy。如果我们要将博客文章按照 tags,categories 归类,tags 和 categories 就是 taxonomy。
- term,术语,分类法中的特定组。比如
#Rust
,#web
就是 tags taxonomy 的 term。 - value,值,与 term 关联的内容。对于 zola 网站来说,就是 HTML 页面,或者说是
section
/page
了。
似乎可以这么理解:
- taxonomy 包含的「类别」称为 term,一个 taxonomy 可以有多个 term。
- term 可以与多个 value 关联。
- value,对于博客来说,value 就是文章了。
拿 tags 这个 taxonomy 来说,它包含的 term 可以有很多,比如 #Rust
,#web
等等。博客文章就是 value,当我们给文章添加 tag,实际上是“给文章添加 tag term”,建立与 term 之间的关联。
如果要启用一个 taxonomy,需要先在 config.toml
里定义它。
taxonomies = [
{ name = "tags" },
{ name = "categories" }
]
配置 taxonomies 时可以使用的字段见 Content / Taxonomies - Configuration。
为了将 taxonomy term 与 HTML 页面关联起来,我们需要在 section
/ page
的 front matter 里添加元数据。
+++
[taxonomies]
tags = ["Rust", "axum", "backend"],
categories = ["tech"]
+++
zola 处理 taxonomy 的逻辑如下:
zola 为每个 taxonomy 生成自己的页面,url 是 <base_url>/<taxonomy_name>
。使用的模板是 templates/<taxonomy_name>/list.html
,如果不存在则使用 templates/taxonomy_list.html
,如果仍不存在则报错。渲染此页面时,模板中可以使用 taxonomy
和 terms
这两个模板变量。terms
是数组,元素的类型是 TaxonomyTerm,可通过它的 page_count
字段访问与 term 关联的页面数量,通过 pages
字段访问所有的关联页面。
zola 为 taxonomy 的每个 term 建立一个页面,url 是 <base_url/<taxonomy_name>/<term>
。使用的模板是 templates/<taxonomy_name>/single.html
,如果不存在则使用 templates/taxonomy_single.html
,如果仍不存在则报错。渲染此页面时,模板中可以使用 taxonomy
和 term
两个模板变量。term
的类型是 TaxonomyTerm,可通过它的 page_count
字段访问与 term 关联的页面数量,通过 pages
字段访问所有的关联页面。
简单地说,zola 为每个 taxonomy 生成两类 HTML 页面。第一类是用于展示 taxonomy 的信息。第二类用于展示 term 的信息,每个 term 都有一个 HTML 页面。
从以上介绍我们也有个隐约的感受,zola 的 taxonomy 是扁平的,而非层级结构。用 taxonomy 来实现博客的 tags 非常方便,依照前面的示例就可以了。如果想要的 categories 也是扁平结构,做法也是差不多的。但如果要实现层级结构的 categories,就比较麻烦了,需要自己写代码进行处理,这里就不提了。
静态文件 static
不论是博客还是其他网站,都需要静态资源,比如 CSS,Javascript 还有图片等等。
zola build 时 static
目录下的文件和子目录,会被原样 copy 到 public
目录。static/style.css
将会变成 public/style.css
;而 static/images/icon.png
将会变成 public/images/icon.png
。以此类推。
在 section
和 page
的 Markdown 文件里,我们可以引用静态文件。比如,引用一张图片:


注意,这里引用是绝对路径(以 /
开头)。
对于博客或者其他小型网站,用这种方式,就足够了。为了支持大型网站,zola 提供了 asset colocation 机制。
colocate 顾名思义就是「放到一起」。把 section
/ page
的 Markdown 文件和所需的静态资源放到一个目录里面。
比如,以下是 content
里面的一个 section
及所属 page
:
└── research
├── latest-experiment
│ ├── index.md
│ └── javascript.js
├── _index.md
└── research.jpg
这里 research
是一个 section
(因为它里面有 _index.md
)。而 latest-experiment
是一个 page
。
按照之前提到的规则,这个 page
必须是 research
目录下的 latest-experiment.md
文件。但这里将 latest-experiment
变成目录并在里面添加 index.md
文件,这样的话它仍然是一个 page
。
在此,我们需要修订一下 section
和 page
的定义:
content
及其子目录下的 Markdown 文件(非_index.md
非index.md
),是page
。content
的子目录,如果里面有index.md
文件,仍然是page
。content
的子目录,如果里面有_index.md
文件,则是section
。content
的其他子目录,不是section
。
现在 section
/ page
都可以是目录了。我们可以把静态资源放到代表它们自己的的目录里面,这就是 asset colocation。在 research section
的 _index.md
里,可以用 
的方式引用这张图片。在 latest-experiment page
的 index.md
里,可以用 [Javascript code](javascript.js)
来引用这个 Javascript 文件。引用 co-locate 的静态资源时,需要使用相对路径。
在 static
目录里,如果我们建立与 asset colocation 时同样的目录结构,也可以达到与 colocation 类似的结果。在 section
/page
里,可以用与 asset colocation 相同的方式来引用静态资源(使用相对路径)。这是因为,此种方式,build 出来的结果,与 asset colocation 时是相同的。
这里简单说一下 zola build
。对于 static
目录,zola build
时会将里面的内容原样 copy 到 public
目录。对于 content
目录,zola build
时,将会生成形如 section-name/section-name/../section_name/page-name/index.html
的 HTML 文件。虽然 HTML 文件名都是 index.html
,但其路径里包含了所有父 section
的名字和 page
的名字。
这样,我们也就明白了,把静态资源放到 section
里与 section
/page
目录层级相同的子目录里面,也可以用相对路径引用的原因了。因为 zola build
之后,与 asset colocation 的结果是相同的。
总结,处理静态资源的 3 种方式:
- 放到
static
目录,用绝对路径引用。 - colocation,放到
section
/page
自己的目录,用相对路径引用。 - 放到
static
里与section
/page
目录层级相同的子目录里面,用相对路径引用。
仔细的我们可能还会发现,zola build
生成的 HTML 文件的名字都是 index.html
,而 zola 渲染页面时,给这些 HTML 文件生成的 url 是形如 <base_url>/.../xxx/
的。url 以 /
结尾,但没带上 index.html
。这时因为,一般的 HTTP 服务器,在进行 url 到文件的映射时,会尝试将 <base_url>/.../xxx/
映射为 .../xxx/index.html
。人们也普通更喜欢 zola 生成的 url 形式。而且,我们在访问的时候,结尾的 /
去掉也没关系的。
主题 themes
主题可以帮助我们快速定制化博客/网站,它提供了完整的 HTML 页面布局,CSS 样式等方案。如果我们没有进行页面设计的能力,不了解 CSS,HTML,Javascript,或者不愿投入过多的精力,那么使用现成的主题就是比较好的选择了。
zola 的主题就是一个完整的 zola 项目,包括前面提到的 content
,templates
,static
等目录及相应的文件。
使用一个主题时,需要把这个主题的 zola 项目放到 themes
目录,然后在配置文件里启动它。zola 的主题一般是个 git 仓库。使用时首先通过 git submodue 将主题添加到 themes
目录里。
# 在项目根目录执行
git submodule add https://github.com/not-matthias/apollo themes/apollo
然后在 config.toml
中指定使用这个主题:
theme = "apollo"
剩下的配置细节,则要根据这个主题提供的文档来进行了。主题常常也会提供配置参数,一般放在 config.toml
的 extra
section 里。
微调主题
zola 的主题,就像一个项目模板。zola 将主题提供的内容,和我们自己提供的内容合并到一起,构建出最终的网站。
我们可以在项目 templates
和 static
目录里提供同名文件,覆盖主题的 templates
和 static
目录里的文件,这样就达到了微调效果。比如:
# 假定我们使用的主题是 apollo
templates/pages/post.html -> 取代 themes/apollo/templates/pages/post.html
templates/macros.html -> 取代 themes/apollo/templates/macros.html
static/js/site.js -> 取代 themes/apollo/static/js/site.js
结合 Tera 的模板继承功能,我们可以仅对模板的某个 block 进行微调。比如我们可以提供 templates/pages/post.html
,对主题的 templates/pages/post.html
的某个 block 进行微调,但其他不变:
{% extends "apollo/templates/pages/post.html" %}
{% block some_block %}
Some custom data
{% endblock %}
这里关键点是,项目 templates
里的模板,可以继承主题里的某个模板。
创建自己的主题
前面已经提到,zola 的主题就是一个完整的 zola 项目。我们可以用 zola init
命令创建主题项目,然后再在项目根目录中添加一个 theme.toml
配置文件,就可以了。
theme.toml
就是 zola 主题和普通 zola 项目的唯一区别了。theme.toml
支持的配置项见Themes / Creating a theme。
正因为 zola 主题是一个完整的 zola 项目,所以开发主题的时候,我们可以随时通过 zola serve
查看效果,debug 等。
开发完成后,我们可以将主题提交到 zola 的主题项目。这样的话,zola 官网的主题页面就会展示我们的主题了。
小结
至此,我们对于 zola 已经有了全面的了解了。 我们知道,SSG 的功能用一句话描述,就是 内容(Markdown)+ 模板 ==> HTML 文件
。
在内容方面,zola 要求将 Markdown 文件保存在 content
目录,并为每个 Markdown 文件生成一个 HTML 文件。zola 的理念是,content
目录的结构,与生成的网站的结构相一致。进一步,zola 将内容分为 section
和 page
。zola 生成的 HTML 页面,要么是一个 section
,要么是一个 page
。
模板文件保存在 templates
目录,我们可以在 Markdown 文件的 front matter 里面指定使用的模板文件,否则就要根据一套规则来确定所使用的模板文件了。渲染不同模板时,我们可以使用不同的模板变量,而且可以通过 {{ __tera_context }}
打印出渲染当前页面时能够使用的全部模板变量,开发调试时使用它非常方便。
zola 内置支持 feed,sitemap 与 robots.txt,简单配置即可。zola 还提供了 taxonomies 机制,可以用来实现 tags 与 categories。
任何网站都离不开静态资源,比如 CSS,Javascript 及图片文件等等。zola 要求我们将静态资源保存到 static
目录,或者通过 asset colocation 的方式将之保存到与 section
/ page
的 Markdown 文件相同的目录里。我们需要注意,引用静态资源时,有时候需要用绝对路径,有时可以用相对路径。
zola 还支持主题,主题也是正常的 zola 项目,只是多了一个 theme.toml
文件。zola 的主题,就像一个项目模板。zola 将主题提供的内容(模板,静态资源等),和我们自己提供的内容(Markdown 文件,模板,静态资源)合并到一起,构建出最终的网站。除了参照主题的文档进行配置,我们也可以在项目 templates
和 static
目录里提供同名文件,覆盖主题的 templates
和 static
目录里的文件,对主题进行微调。
有了这些知识,我们就可以完全按照自己的喜好来搭建博客了。如果想要动手写一个 SSG,这些知识也能派上用场。对于博客,最简单还是找一个喜欢的主题,然后开始配置。如果你有想法,也可以设计自己的博客主题。
部署
zola 生成的是静态网站,即 public
目录的那些 HTML(还有 CSS,Javascript,图片等等) 文件,部署是很容易的。我们既可以选择部署到自己的服务器,也可以部署到 GitHub Pages 等平台。zola 的 Deploy 文档页面列出了在各个平台部署的详细步骤,参阅即可。
我的选择是,尽量使用 Cloudflare。流量通过 Cloudflare 接入,博客部署到 Cloudflare Pages,并且使用 Cloudflare Workers 作为反向代理,进行流量路由。这是考虑到以下需求:
- 博客需要使用的域名 yfaming.com 是一个 apex 域名。
- yfaming.com 域名下,以后需要添加新的路径来提供其他服务。
比如,我准备近期就在这个域名下搭建一个基于 NWC 的 lightning address 服务,这样就可以将 yfaming@yfaming.com
作为我的 lightning address 了。这个服务需要使用 https://yfaming.com/.well-know/lnurlp/
路径。这意味着,我需要至少保留按照 URL 前缀进行路由的能力。
使用 Cloudflare 接入,还可以免费享受 CDN 加速,HTTPS 证书,和一些安全防护措施(比如防 DDoS,隐藏服务器 IP)。
考虑到很快需要在自己的服务上搭建 lightning address 服务,那么直接把博客部署在自己的服务也是 OK 的。但是既然 Cloudflare Pages 满足需求,而且能够方便地做到当博客 GitHub 仓库更新后,立即触发部署,使用 Cloudflare Pages 还是有优势的。
对于开发者来说,GitHub 是我们最熟悉的平台,为什么不部署到 GitHub Pages 呢?因为我博客使用的域名 yfaming.com 是 apex 域名。apex 域名不能添加 CNAME 记录。部署到 GitHub Pages 的话,只能给这个域名添加 A 记录,其 IP 为 GitHub Pages 的 IP。但这样的话,GitHub Pages 就成为这个域名的流量入口了,无法根据 URL 进行路由。(只有使用子域名,才可以通过 CNAME 的方式绑定域名。)