跳转到主要内容

使用 Oink 创作优美的内容

一本实战教程:用 OINK 创作清晰、优美且易于维护的技术内容。

使用 Oink 创作优美的内容

一本实战教程:用 OINK 创作清晰、优美且易于维护的技术内容。

《使用 Oink 创作优美的内容》是 OINK 参考文档的教程伴侣。参考文档解释每个参数和组件的作用; 本书正在沿着一个 Starter 站点,逐步编写从本地预览到评审与发布的练习。

前三章已放入可直接操作的内容。后续章节在完整演练写作期间,会刻意展示 Book 的草稿状态。

阅读方式

从第 1 章:跑起第一个站点开始。第 1–3 章沿用同一个 Starter, 依次完成预览、新建双语页面和完善正文。第 4–6 章与附录仍为提纲草稿;需要现在完成 定制与部署时,请继续Starter 教程,再按发布上线操作。 下方对象索引同时演示 Book 的出版能力,可在需要查图表和示例时使用。

目录

图目录

  1. 图 1-1 — 文档站效果示意。你的 Starter 预览使用中性示例内容;第一个里程碑是能打开并修改的站点。

表目录

  1. 表 2-1 — 同一个显式顺序被导航、翻页与生成目录共同使用。
  2. 表 6-1 — 每种交付状态都有自己的证据与交接要求。
  3. 表 A-1 — 同一棵内容树可以生成多种面向不同用途的 Book 输出。

公式目录

  1. 公式 3.1 — 清晰度、准确性或一致性中任何一项降为零,整个页面就会失败。

示例目录

  1. 示例 3-1 — 同一页面现在明确了前置条件、命令与可见结果。

1 从一个能运行的站点开始

安装前置工具,启动本地预览,在调整设计前先建立可见的基线。

好的教程首先要给读者一个看得见的结果。对 OINK 而言,这个结果是由 Hugo Extended 在本地提供的官方 Starter——此时还没有改标识、配色、语言组合或内容架构。

明确结果

完成本章时,你应该拥有英文、中文、法语首页,可用的 Docs、Blog、Book 路由,本地搜索, 以及颜色模式控件。这条小基线足以在后续工作中区分内容问题、主题问题与部署问题。

OINK 文档站的主题效果示意,并非 Starter 预览截图
图 1-1 文档站效果示意。你的 Starter 预览使用中性示例内容;第一个里程碑是能打开并修改的站点。

安装前置工具

当前 Starter 需要 Git、Go 1.27 或更新版本,以及 Hugo Extended 0.165.0 或更新版本。 OINK 声明的较低兼容下限仍是 0.160.1,但 Starter 与它的 workflow 有意固定当前持续测试 工具链。不需要 Node.js。

$ go version
go version go1.27.0 darwin/arm64
$ hugo version
hugo v0.165.0+extended+withdeploy darwin/arm64

启动本地预览

真实项目应通过 GitHub 的 Use this template 操作创建仓库。只在本地评估原始模板时, 克隆并启动 Hugo:

$ git clone https://github.com/pgsty/oink-starter.git my-docs
$ cd my-docs
$ hugo server

打开 Hugo 输出的地址,修改 data/home/en.yaml 里的一句话,再确认浏览器已经显示变更。 一个能对内容修改作出响应的预览,比终端里只显示“服务已启动”更有证明力。

记录基线

在开始定制前,记录四个事实:Hugo 版本、go.mod 中的主题版本、正在评审的 commit, 以及你实际打开的路由。第 2 章会在不丢失这条基线的前提下,把运行中的站点组织成内容树。

完整分层流程见使用 OINK Starter,不采用模板的安装方式见 从零建站。

2 为内容建立结构

让目录、分区索引、页面包与权重共同构成可预期的阅读与导航顺序。

对普通站点而言,OINK 不会另外维护一份导航数据库。内容树就是侧栏树,同一个顺序还会驱动翻页器 与 Book 目录。读者不应该对“下一页是什么”得到三个不同答案。

从读者的问题出发

一级分区应使用读者能辨认的任务或主题命名。小型工程站点通常需要上手指南、参考文档、 运维指南与变更记录。只有当一个目录能为若干页面提供有意义的共享上下文时,才应创建它。

继续使用第 1 章的 Starter,保留已有示例。本章在现有 Docs 分区下新增一篇英文页面及其 中文译文。法语可以保持启用;本练习只添加下面两种语言的对页。

搭建内容树

本练习涉及的文件;其它 Starter 文件保持不变

  • content/
    • docs/
      • _index.md已有分区根页
      • _index.zh.md已有中文根页
      • preview-check.md新增页面
      • preview-check.zh.md新增译文

按下面的完整内容创建两个文件。分区根页已经存在,不要替换它们。

content/docs/preview-check.md
---
title: Verify a local preview
description: Check that a documentation edit reaches the browser.
weight: 25
---

## Check the preview {#check-preview}

Open this page locally, change this sentence, and confirm the browser updates.
content/docs/preview-check.zh.md
---
title: 验证本地预览
description: 确认文档修改已经显示在浏览器中。
weight: 25
---

## 检查预览 {#check-preview}

在本地打开本页,修改这句话,再确认浏览器已显示新内容。

译文以 .zh.md 后缀放在英文源文件旁边。这两页没有图片或下载资源,使用独立 Markdown 文件即可;页面拥有这些资源时,再使用页面包。

明确写出顺序

现有栏目权重之间留有空档。新页面使用 25,可以插入相邻栏目之间,而不必重新编号;两种译文保持相同权重。新建内容树时,以 10 为间隔也能留下类似空间:

项目 权重 为什么放在这里
快速上手 10 建立可运行的基线
创作内容 20 在可运行站点上继续搭建
定制站点 30 在结构之后改变呈现
运行维护 40 验证并发布结果
表 2-1 同一个显式顺序被导航、翻页与生成目录共同使用。

建立稳定地址

任何可能被其它页面引用的标题,都要显式写出 ID。英文页面与中文页面虽然显示不同的标题, 却使用同一个 ID。这会让链接、页内目录与整书打印在两种语言中始终对齐。

保持 hugo server 运行,打开 /docs/preview-check/ 与 /zh/docs/preview-check/。 两页都应出现在各自的 Docs 侧栏中,语言切换应打开对应译文;两页标题的锚点均为 #check-preview。保留这两个文件,第 3 章继续修改它们。

完整规则见编写页面与组织内容。

3 组合出值得阅读的页面

把正文、提示块、代码、媒体、表格与数学公式组合在一起,而不把页面变成组件目录。

组件应该帮助论证,而不是与内容争夺注意力。先写普通正文,只有当读者需要比较、验证、复制 或停下来思考时,才引入额外结构。

让每个内容块只做一件事

先写出那句话

如果你无法用一句话解释某个组件为什么应该出现在这里, 那就先保留普通正文,直到需求变得明确。

用提示块表达前置条件或风险,用表格对齐重复字段,用代码块放置读者可以执行的材料, 只在形状或空间关系承载了正文无法表达的信息时才使用图片。

从一份小型页面契约开始

继续修改第 2 章的 content/docs/preview-check.zh.md,用下面的完整示例替换内容。 保留预览服务运行,在第二个终端的站点仓库目录中执行页面里的构建命令。

示例 3-1 同一页面现在明确了前置条件、命令与可见结果。
content/docs/preview-check.zh.md
---
title: 验证本地预览
description: 确认文档修改已显示在浏览器中,并通过无警告构建。
weight: 25
---

## 检查预览 {#check-preview}

> [!NOTE] 保持预览服务运行
> 在第二个终端中进入站点仓库,再执行下方构建。

```bash
hugo --environment production --panicOnWarning
```

命令应成功退出且没有警告。刷新本页
`http://localhost:1313/zh/docs/preview-check/`,确认新提示块与命令已显示。
构建成功与可见内容已更新,需要分别确认。

在 preview-check.md 中用英文补上相同任务与命令,保留 weight: 25 和 #check-preview,本地 URL 使用 /docs/preview-check/。标题命名任务,摘要说明结果, 提示块解释命令在哪执行。再次打开双语页面并检查语言切换,再继续添加组件。

不用装饰数量衡量质量

有用的页面需要同时平衡三项独立属性:

Q=Cclarity×Aaccuracy×Kconsistency Q = C_{clarity} \times A_{accuracy} \times K_{consistency}
公式 3.1 清晰度、准确性或一致性中任何一项降为零,整个页面就会失败。

这里刻意使用乘法:视觉精美无法弥补错误命令,准确的正文在读者找不到或无法按步骤执行时, 同样会失败。

连接证据

用 示例 3-1 作为源码模式, 再用 公式 3.1 作为评审问题。第 4 章概述下一阶段的视觉设计;现在需要继续操作时,按Starter 分层定制完成下一步。

组件参考的起点是组件。只有当教程引入了某项真实需求时, 才需要阅读对应组件的独立页面。

4 塑造阅读体验

把稳固的内容结构变成一份可辨识、响应式且双语对齐的出版物。

设计应该在内容树已经可用之后开始。本章将把品牌、首页编排、导航、排版、页面宽度与语言行为 连成一套可评审的系统。

建立视觉层级

先处理标题、导语、正文、层级标题与局部导航。在加入色彩或装饰前,读者就应该明白下一个动作由哪个区域承接。 OINK 提供层级,站点变量提供身份。

完整演练将替换站点名、标识、字标、favicon、强调色与字体,同时确保深浅两种颜色模式都清晰可读。

编排首页

首页是数据,而不是一份一次性模板。YAML 分区注册表应该讲述一个简短故事:项目是什么、服务谁、 读者下一步能做什么,以及在哪里可以看到主题的真实使用案例。

本节的完整版会从双语 data/home 文件中组装 Hero、组件矩阵、Case 画廊与最终行动入口。

保持导航可预期

顶部菜单、外壳根切换器、侧栏、页内大纲与翻页器各自回答不同问题。在桌面端与移动端同时评审它们, 并确保两种语言显示同一套内容顺序。

同时设计两种语言

译文是对等页面,不是最后的收尾步骤。在把面向读者的文字译成自然中文时,保持路由语义、显式标题 ID、 菜单顺序、图片与功能参数不变。

在完整演练落地前,请参考品牌与外观、 首页与落地页与多语言。

5 发布不止于参考页面的内容

用 Docs、Blog、Case、Book 与发布页面分别回答读者的不同需求。

同一个站点可以发布多种知识,而不必把它们强行塞进同一种布局。内容类型选择页面外壳, front matter 变体则在同一外壳内调整呈现。

让内容类型匹配读者

  • Docs 回答任务或参考问题,并显示它在内容树中的位置。
  • Blog 是带日期、作者、分类法、订阅源与分享能力的文章。
  • Case 说明真实站点如何应用主题。
  • Book 章节组成有意设计的阅读顺序,并提供稳定交叉引用。
  • 发布注记把版本、迁移方法与验证证据连接起来。

配置 Blog 家族

普通 Blog 分区可以选择行列表、卡片或表格。需要沉浸式开场的分区仍然保留同一类型, 只改变四个相互独立的呈现键:

type: blog
featured_image: hero
toc_style: flow
toc_taxonomies: false
sidebar_enabled: false

这就是沉浸式阅读所演示的契约。没有第二种 Article 类型, 也没有被复制的发布流水线。

把样例变成 Case 案例

Case 索引使用 Blog 卡片形式,每个内部页面则先说明站点、源码、语言模式、规模与 OINK 能力, 再链接到线上成果。保留内部说明页,能让 showcase 成为文档的一部分,而不只是一面外链标识墙。

让 Book 与 Docs 相互补充

参考页面保持完备,并能独立搜索。教程则从参考中选出一条路径,每次只引入一项决策, 在读者需要完整参数表时再链接回参考文档。

完整演练将使用同一组源事实,新增一篇文章、一个 Case 案例与一篇短 Book 章节, 再比较三者的阅读体验。

6 有把握地交付

区分本地预览、仓库集成、主题发布与站点部署,并用对应证据验证每一种状态。

发布是一串可以分别验证的状态。本地预览成功,只能证明内容与主题可以在当前工作区共同渲染; 它并不能证明远端模块标签已经存在,也不能证明公开站点已经部署了这一版本。

为每种交付状态命名

状态 证据 不能证明什么
本地预览 站点可以使用指定的本地主题检出完成渲染 公开主题版本已经发布
站点集成 内容、配置与依赖变更已经一起审阅 托管平台已经部署这些变更
主题发布 不使用本地替换时,公开标签与模块校验和可以解析 消费站点已经升级
站点部署 公开版本与代表性路由可以访问 每种语言和视口都正确
表 6-1 每种交付状态都有自己的证据与交接要求。

验证最小且有效的范围

在自己的 Starter 仓库中,先执行普通生产构建,再使用已选定的部署 workflow:

hugo --cleanDestinationDir --gc --minify --environment production \
  --printPathWarnings --panicOnWarning

确认 hugo mod graph 解析到 go.mod 中预期的公开版本,再按Starter 部署步骤操作。 普通 Starter 站点不需要 npm 构建脚本或同级主题 checkout。如果同时修改 OINK 主题本身, 另按主题开发流程验证。 分别记录本地构建、workflow 结果与公开 URL 检查。

审阅真正渲染出的结果

自动检查可以发现坏链接、重复 ID、无效短代码与无障碍回归,却无法判断 Hero 裁切是否合适, 也无法判断密集表格在手机上是否仍然易读。请在桌面与窄屏下抽查具有代表性的中英文路由, 覆盖导航、主题控件、代码块以及整书输出。

交接事实,而不是暗示

有效的交接应列出变更文件、命令与结果、已知限制,以及尚未发生的下一种状态。引用 表 6-1,准确说明当前到达了哪一步, 不要用一个“完成”混淆验证、发布与部署。

完整的运维参考见预览站点、 部署站点与排查构建问题。

A Front Matter 模式

可复制调整的 Book 根页、章节、沉浸式 Blog 文章与整书输出契约。

这些模式刻意保持精简。先复制建立内容契约所需的字段;只有在真实的读者需求出现时, 才加入额外的展示选项。

Book 栏目根页

type: book
book_kind: book
outputs: [HTML, print, markdown]
cascade:
  type: book
  book_draft_banner: true

根页声明 Book 外壳与生成输出。它不需要章节编号;编号属于阅读顺序中真正出现的内容。

Book 章节

book_kind: chapter
book_number: 1
book_status: draft
weight: 10

使用 book_status: draft 表达可见的编辑状态。它与 Hugo 的 draft: true 不同: 页面会保留在普通构建中,审阅者仍然可以阅读尚未完成的章节。

沉浸式 Blog 文章

type: blog
authors: [oink, vonng]
featured_image: hero
toc_style: flow
toc_taxonomies: false
sidebar_enabled: false

文章仍然属于 Blog 家族,Feed、作者、系列与分享能力都会保留;以上字段只改变阅读呈现。

生成输出矩阵

输出 范围 典型用途
HTML 单个根页或章节 阅读、导航与搜索
print 完整的 Book 审阅、打印与 PDF 转换
markdown 保留源码结构的 Book 导出与下游处理
表 A-1 同一棵内容树可以生成多种面向不同用途的 Book 输出。

Book 根页生成的目录、插图、表格、公式与示例索引会一起证明这些契约。中英文页面必须对齐 显式标题 ID 与对象 ID,确保每种格式都保留相同的引用关系。