Skip to content

Commit e747993

Browse files
committed
polish
1 parent f9e138c commit e747993

File tree

2 files changed

+211
-166
lines changed

2 files changed

+211
-166
lines changed

content/post/2025-07-19-zigcc-zine-migration.smd

Lines changed: 0 additions & 166 deletions
This file was deleted.
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
---
2+
.title = "ZigCC 网站迁移至 Zine 实战复盘",
3+
.date = @date("2025-07-19"),
4+
.author = "xihale",
5+
.layout = "post.shtml",
6+
---
7+
8+
本文复盘了 ZigCC 社区网站从 Hugo 迁移至 Zine 的全过程,旨在分享其中的经验与思考,为有类似需求的朋友提供参考。
9+
10+
# [为何选择 Zine?]($section.id("为何选择-Zine"))
11+
12+
我们最初使用 Hugo 及其 Docsy 主题搭建网站,但在使用过程中遇到了一些痛点,促使我们寻找新的解决方案。选择迁移至 Zine 主要基于以下几点考量:
13+
14+
1. **解决 Hugo 的既有问题**:Hugo 本身非常强大,但在某些细节上存在问题,例如 Markdown 在页脚(footer)中的解析行为不符合预期。
15+
2. **追求现代设计美学**:Docsy 主题乃至 Hugo 的部分生态,在技术和审美上略显陈旧。我们希望网站风格更加现代化、简约。
16+
3. **更精细的代码高亮**:主流高亮库如 `highlight.js`, `prism.js` 和 `shiki.js` 中,`shiki.js` 效果最佳。然而,Zine 对 Zig 代码的语法高亮比它们更为细致,提供了更强的自定义控制能力。(代价是 Zine 对其他语言的解析支持尚不完善,其底层似乎是调用 tree-sitter 实现的)。
17+
4. **灵活的内容格式生成**:Zine 的布局(Layout)和 Scripty API 赋予了项目结构极大的灵活性,使得未来生成 PDF 或其他格式的文档变得可行,RSS Feed 的生成也同样受益。
18+
5. **显著的性能提升**:迁移后,无论是后端的构建速度还是前端的渲染性能都有了明显提升。这部分也得益于 Zine 架构带来的项目结构简化。
19+
20+
当然,还有一个至关重要的原因:**拥抱并反哺 Zig 生态**。正如 ZigCC 发起者刘家财所说:
21+
> “如果我们自己都不用,那更不会有别人用了。”
22+
23+
作为 Zig 社区的一份子,我们有责任参与到生态建设中。果不其然,这次迁移过程也让我们发现了 Zine 的一些待解决问题和潜在的改进点。
24+
25+
# [Zine 简介]($section.id("Zine简介"))
26+
27+
Zine 是一个现代、高效的静态网站生成器。理解其核心组织结构是上手的第一步。一个最简化的 Zine 项目(通过 `zine init` 创建并删减后)目录结构如下:
28+
29+
```sh
30+
.
31+
├── assets/ # 存放 CSS、图片等静态资源
32+
├── content/ # 存放网站内容,通常是 smd 文件
33+
│   ├── about.smd
34+
│   └── index.smd
35+
├── layouts/ # 存放布局模板,每个 smd 文件都对应一个 shtml 布局
36+
│   ├── index.shtml
37+
│   ├── page.shtml
38+
│   └── templates/ # 模板可以被其他布局继承,以实现代码复用
39+
│   └── base.shtml
40+
└── zine.ziggy # Zine 项目的全局配置文件
41+
```
42+
43+
上面的目录结构和 Hugo 类似, `zine.ziggy` 是网站的配置文件,ZigCC 的配置如下:
44+
```zig
45+
Site {
46+
.title = "Zig 语言中文社区",
47+
.host_url = "https://ziglang.cc",
48+
.content_dir_path = "content",
49+
.layouts_dir_path = "layouts",
50+
.assets_dir_path = "assets",
51+
.static_assets = [],
52+
}
53+
```
54+
55+
在 HTML 渲染方面,Zine 与 Hugo 相差甚远,Zine 自创了两种新格式来进行数据的组织:
56+
- [SuperMD](https://zine-ssg.io/docs/supermd/) 格式 - 扩展了标准 Markdown,允许定义嵌入式资源和语义结构,解决了传统 Markdown 无法表达复杂内容而不使用内联 HTML 的局限性。
57+
- [SuperHTML](https://zine-ssg.io/docs/superhtml) 模板系统 - 扩展的 HTML 格式专注于正确的模板逻辑表达。设计确保“不可能生成语法错误的 HTML,大多数错误会在构建时被发现”。
58+
59+
这两者本质上就是 markdown 与 html,Zine 做的增强是为它们实现了其自创的表达式语言 [scripty](https://zine-ssg.io/docs/scripty/)。因此下面重点介绍 Scripty 的用法。
60+
61+
## [Scripty]($section.id('scripty'))
62+
63+
Scripty 是一个非常小的语言, 专门用于嵌入到字符串中,为 SuperMD 和 SuperHTML 提供动态内容生成能力。 因此 Scripty 没有任何类型的语句或代码块,只能用于构建表达式。
64+
- 所有表达式都以 `$` 开头
65+
- 支持字段导航: `$foo.bar.baz`
66+
- 支持函数调用: `$foo.bar.qux('arg1', "arg2")`
67+
- 支持基本字面量:字符串、整数、浮点数、布尔值
68+
69+
这里举一示例来说明 Scripty 在 SuperMD 和 SuperHTML 中的写法:
70+
71+
```html
72+
<ctx about="$site.page('about')">
73+
<a href="$ctx.about.link()" text="$ctx.about.title"></a>
74+
</ctx>
75+
```
76+
77+
```markdown
78+
Check out our [about page]($link.page('about')).
79+
```
80+
81+
既然是表达式语言,scripty 在多层嵌套时才能体现其威力:
82+
83+
```html
84+
<h1 class="$page.title.len().gt(25).then('long')">...</h1>
85+
```
86+
87+
如果当前页面标题长度小于 25 时,就有输出:
88+
89+
```html
90+
<h1 class="long">...</h1>
91+
```
92+
93+
关于 SuperMD 和 SuperHTML 的用法这里不再赘述,推荐大家去阅读 Zine 的官方文章。
94+
95+
# [迁移步骤详解]($section.id("迁移步骤详解"))
96+
97+
整个迁移过程可以分为内容转换、布局设计、预览调试和最终部署几个关键步骤。
98+
99+
## [内容格式转换]($section.id("内容格式转换"))
100+
101+
迁移的核心工作是将原有内容(本文中主要是 Markdown 和 Org Mode 文件)转换为 Zine 支持的 SuperMarkdown (`.smd`) 格式。
102+
103+
起初,我尝试让 AI 编写 `md` 到 `smd` 的转换脚本,效果尚可。但对于结构更复杂的 Org Mode 文件,脚本难以胜任,因此最终转向使用 Pandoc。
104+
105+
以下是使用 Pandoc 将 `.org` 文件批量转换为 Markdown 格式(并重命名为 `.smd`)的 Fish 脚本,此方法同样适用于 `.md` 文件:
106+
107+
```fish
108+
# 注意:这里实际上是转换到了 GitHub Flavored Markdown (gfm) 格式
109+
# 后续仍需手动调整以完全适配 smd 语法
110+
for f in *.org
111+
pandoc -s $f -t gfm -o (path change-extension "smd" $f)
112+
end
113+
```
114+
115+
**手动适配要点**:
116+
117+
Pandoc 完成初步转换后,仍需手动调整以适配 `.smd` 和 `.shtml` 的专属语法。常见差异包括:
118+
119+
1. **HTML 嵌入**:`.smd` 不推荐直接嵌入 HTML。相关代码需要用 ` ```=html` 代码块包裹。
120+
2. **章节标题链接**:标题不会自动生成 ID 和锚点链接,需要手动使用 `$section.id("custom-id")` 添加(注意 ID 不能包含空格)。
121+
3. **资源引用**:可以使用 Scripty API 或将资源放在 `public` 目录下通过绝对路径 (`/path/to/resource`) 引用。
122+
123+
建议先完成布局文件的编写,在实时预览的环境下再进行这些细致的手动适配,效率更高。
124+
125+
## [处理 Frontmatter]($section.id("处理-Frontmatter"))
126+
127+
Zine 使用 Ziggy 语法作为 Frontmatter 的格式,它与 Zig 语法高度相似。
128+
129+
> [Ziggy 官网](https://ziggy-lang.io/) 提供了一个方便的 [在线转换器](https://ziggy-lang.io/documentation/ziggy-convert/),可以将 YAML、TOML 或 JSON 格式的 Frontmatter 转换为 Ziggy。
130+
131+
一个典型的 `.smd` 文件 Frontmatter 如下:
132+
133+
```zig
134+
---
135+
.title = "Zig comptime",
136+
.date = @date("2025-01-23T12:00:00+08:00"),
137+
.author = "xihale",
138+
.layout = "post.shtml",
139+
.draft = false,
140+
---
141+
```
142+
143+
## [设计布局]($section.id("设计布局"))
144+
145+
由于我们希望采用全新的简约风格,因此我没有沿用 Docsy 的样式,而是重新设计了一套布局。目前的版本虽已上线,但在目录(TOC)等细节上仍有优化空间。
146+
147+
Zine 布局有几个关键特性:
148+
149+
1. **ID Slot**:Zine 采用 [ID Slot](https://zine-ssg.io/docs/superhtml/#super) 机制,比传统的 Slot 更灵活。例如,你可以将 `<head>` 和内容区的 Slot 分开处理,从而实现对资源加载等操作的精细控制。
150+
2. **`<ctx>` 标签**:合理使用 `<ctx>` 标签可以简化模板的逻辑和组织结构。
151+
152+
此外,你还可以通过在 `.smd` 的 Frontmatter 中定义 `.custom` 字段,向布局传递自定义参数,实现更细粒度的控制。
153+
154+
例如,在 `.smd` 中启用数学公式支持:
155+
156+
```zig
157+
// file: content/your-post.smd
158+
.custom = .{
159+
.math = true,
160+
},
161+
```
162+
163+
然后在布局文件中根据此标志加载 KaTeX 库:
164+
165+
```html
166+
// file: layouts/post.shtml
167+
<ctx :if="$page.custom.getOr('math', false)">
168+
<link
169+
href="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/KaTeX/0.15.2/katex.min.css"
170+
crossorigin="anonymous"
171+
rel="stylesheet"
172+
/>
173+
<script
174+
defer
175+
src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/KaTeX/0.15.2/katex.min.js"
176+
crossorigin="anonymous"
177+
></script>
178+
<script
179+
defer
180+
src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/KaTeX/0.15.2/contrib/auto-render.min.js"
181+
crossorigin="anonymous"
182+
onload="renderMathInElement(document.body);"
183+
></script>
184+
</ctx>
185+
```
186+
187+
## [预览与调试]($section.id("预览与调试"))
188+
189+
完成布局和初步内容转换后,就进入了最繁琐的环节:预览、检查和修正。
190+
191+
运行 `zine` 命令启动本地服务器,实时预览网站效果。逐一检查每篇文章的渲染情况,发现格式错误或显示异常的地方,返回 `.smd` 文件进行修改。
192+
193+
这一步需要极大的耐心,几乎等同于重新校对所有文章。需要注意的是,Zine 目前仍在快速发展中,部分功能的报错信息可能不够明确,需要结合文档和实践耐心排查。
194+
195+
## [部署]($section.id("部署"))
196+
197+
Zine 官方文档提供了详细的部署指南:
198+
199+
- [Deploying to GitHub Pages](https://zine-ssg.io/docs/deploying/github-pages/)
200+
- [Deploying to Cloudflare Pages](https://zine-ssg.io/docs/deploying/cloudflare-pages/)
201+
202+
你可以参考 zine-ssg 或本站(ZigCC)仓库中的 GitHub Actions 配置来设置自己的自动化部署流程。
203+
204+
# [四、总结与经验分享]($section.id("总结与经验分享"))
205+
206+
最后,分享几点额外的经验:
207+
208+
1. SuperHTML (`.shtml`) 对 HTML 语法有严格的要求,它不仅仅是 HTML 的超集。某些写法可能与个人习惯冲突,需要适应其规范。
209+
2. Scripty API 采用链式调用语法,非常灵活。
210+
211+
迁移过程中遇到问题时,不要慌张。首先仔细查看终端的报错信息,然后查阅官方文档,最后可以多翻阅 zine-ssg 和 ziglang.org 等同样使用 Zine 构建的网站源码,它们是最好的学习范例。

0 commit comments

Comments
 (0)