# Oink: 用Markdown快速创建美观的现代网站

LLMS 索引： [llms.txt](/llms.txt)

---

十几天前，我只是想给几个新项目找一套顺眼的文档主题。

十几天后，我把自己的 **18 个网站全部迁了过去**。

从 Pigsty 的中英文文档、Silo、PIG、SOW、PG Exporter，到公司首页、个人博客，再到《设计数据密集型应用》这样的多语言书籍，现在全部运行在同一套框架上。

![图片](01.webp)

这可不是什么换个颜色、改个字体的小装修。十八个网站的内容结构、语言、规模和用途各不相同：有上千个页面的大型技术文档，有只有几页的小工具，有博客，有书籍，有下载站，还有纯粹用来做产品介绍的 Landing Page。

把它们全部迁过去，是一个相当折腾的工程。

但一个作者对工具最高级别的评价，不是 README 里写了多少卖点，也不是给自己点了多少个 Star，而是：**敢不敢把自己的生产环境全部押上去。**

现在我押了。所以，我觉得 Oink 终于可以正式发布了。

![图片](02.webp)

![图片](03.webp)

![图片](04.webp)

![图片](05.webp)

![图片](06.webp)

![图片](07.webp)

![图片](08.webp)

![图片](09.webp)

------------------------------------------------------------------------

### Oink 到底是什么？

Oink 是一个开箱即用、本地优先的 Markdown 文档框架。

你只需要三样东西：

Markdown

Hugo Extended 二进制

一个 Git 仓库。

然后就可以得到一套现代、完整、可搜索、可打印、支持深浅色、多语言、多版本，并且可以直接部署到 GitHub Pages、Cloudflare Pages、Nginx 或任何静态服务器上的网站，通过 Git 推送直接发布更新。

整个构建过程不需要 Node.js，不需要 `npm install`，不需要 PostCSS，不需要外部 CDN，也没有必须常驻的后端服务。主题所需的字体、样式、图标与交互运行时全部随项目本地交付，一条 `hugo` 命令就能生成完整的静态目录。断网也能构建与服务。

当然，你也可以完全让 Claude 与 Codex 来操心这些事。

![图片](10.webp)

它当然可以被称为一个 Hugo Theme，但我觉得这个说法有点低估它。我更愿意把 Oink 称为一套 **文档发行版**。普通主题解决的是“页面长什么样”，Oink 想解决的则是整套问题：

•内容应该怎么组织；•文档、博客和书籍如何共存；•搜索、导航、多语言和版本管理怎么做；•技术组件应该怎样书写；•同一份内容如何输出给浏览器、打印机和 AI Agent；•网站如何构建、检查、部署、升级和长期维护。

![图片](11.webp)

换句话说，它不是只给 Docsy 换了一张脸，而是把我这些年维护工程文档时踩过的坑、形成的判断和积累的组件，封装成了一条完整的交付路径。

**Markdown in, Modern Docs out。**

这就是 Oink 最核心的产品定义。

至于名字，也很简单：一边是文档与墨水的 **Ink**，另一边是 Pigsty 宇宙里小猪的叫声**Oink**。README 里那句**Open, Indexed, Navigable, Knowledge**，就当作附赠的彩蛋好了。

------------------------------------------------------------------------

### 为什么偏偏是现在？

因为现在写代码实在是太快了。

以前做一个新开源项目，最费时间的通常是把功能做出来。现在有了 Codex、Claude Code 和各种 Agent，项目原型可能一个下午就冒出来了，仓库、工具、组件和小产品的数量正在迅速膨胀。

但项目做完之后，问题才刚刚开始：

README 要写，安装指南要写，配置参数要写，发布注记要写，API 文档要写，博客和产品首页最好也得有。否则代码虽然存在，别人却不知道它是什么、能解决什么问题、该怎么用，更不知道出了问题该去哪里找答案。

**AI 大幅降低了生产代码的成本，却没有自动消灭文档。恰恰相反，它制造了更多需要被解释、被组织、被交付的东西。**

文档正在从过去的附属品，变成项目交付链条里越来越明显的瓶颈。

![图片](12.webp)

另一方面，Markdown 也正在获得一种新的地位。

过去我们说 Markdown 是程序员写文档的通用格式；现在，它实际上已经成了人类与 AI Agent 之间的公共协议。README、AGENTS.md、Skills、任务说明、项目记忆、设计文档、知识库，越来越多的信息最终都落在 Markdown 里。

这是一个很重要的变化。

以前的文档框架，主要考虑的是怎样把内容渲染给浏览器里的人看。现在，一个真正现代的文档系统，还应该考虑：

•AI 能不能直接读；•内容能不能无损复制；•页面有没有干净的 Markdown 版本；•整个站点有没有机器可发现的索引；•源文件里是不是混进了大量只对某个前端框架有意义的噪音。

Oink 会为每个页面生成对应的 `.md` 版本，在站点根目录生成 `llms.txt`，并提供“复制为 Markdown”“查看 Markdown 源码”以及可选的“在 ChatGPT / Claude 中打开”入口。这些都是构建时生成的静态产物，不需要额外服务，也不会把正文偷偷上传到什么地方。

所以 Oink 并不是简单地“支持 AI”。

它的内容模型从一开始就在假设：**这份文档既要给人看，也要给机器读。**

------------------------------------------------------------------------

### 我受够了现代文档框架

做 Oink 还有一个非常朴素的原因：我确实受够了现有的文档框架。

这些框架并不是不能用，其中不少甚至相当优秀。问题在于，它们往往要求你在一些我不愿意接受的东西之间做选择。

#### MDX：为了表现力，把内容变成私有方言

第一类是大量使用 MDX 的框架。

我理解 MDX 的吸引力。你可以在 Markdown 里直接插入 React 组件，想画什么就画什么，表现力几乎没有上限。

![图片](13.webp)

但它的代价也非常明确：内容和表现逻辑开始纠缠在一起。

文档里逐渐出现组件标签、属性、嵌套容器、导入语句和各种只有特定框架才能理解的东西。写到最后，它看上去不再像 Markdown，而像一份披着 Markdown 外衣的 JSX。

![图片](14.webp)

这会带来三个问题。

第一，人直接阅读源文件时，噪音太多。

第二，AI Agent 读取时，要浪费上下文理解大量与内容无关的表现细节。

第三，一旦你想换框架，真正被锁住的不是页面样式，而是内容本身。

我始终认为：**样式控制不应该大面积侵入内容。**

内容是长期资产，主题只是它某一阶段的衣服。为了今天一个漂亮的选项卡，把十年后仍然有价值的文字写成某个前端框架的私有 DSL，这笔账不划算。

#### Node.js：为了写文档，先养一套前端工程

第二类问题是现代前端工具链。

你只是想写几篇文档，结果先得到一个 `package.json`、两个锁文件、几百兆 `node_modules`，再配上 bundler、插件、主题包和一串版本约束。

哪怕网站最终只是一些静态页面，构建它的过程也越来越像在维护一个前端应用。

这当然不是 Node.js 的原罪。对于真正复杂的 Web 应用，这些工具有充分价值。但对文档来说，我认为它们经常是明显的过度设计。

文档本来应该是一个项目里：

•最容易构建的东西；•最容易搬走的东西；•最容易离线保存的东西；•最不应该因为依赖升级突然坏掉的东西。

结果现在恰好反了。

代码还能编译，文档站先因为某个插件、锁文件或者构建环境开始闹脾气。你只是想改个错别字，却先要研究为什么今天的构建和半年前不一样。

我不喜欢这种感觉。

#### 轻量框架不够用，完整框架又不好看

还有一类像 Docsify 这样的轻量工具，确实简单，但当你需要大型导航、多语言、多版本、打印、SEO、复杂组件、书籍交叉引用和完整发布体系时，很快就会感觉表现力不足。

Docsy 则正好相反。它的内容模型和工程能力很成熟，毕竟是 Google 发起、CNCF 项目大量采用的一套方案，但它的默认界面确实已经很有年代感，而且很多实现仍然带着旧时代前端工程的包袱。

![图片](15.webp)

Hextra 和 Blowfish 我也都用过，而且挺喜欢。前者适合文档和书籍，后者适合博客，但当我同时维护十几个网站时，我不想再维护三套内容方言、三套配置方式和三套定制逻辑。

![图片](16.webp)

![图片](17.webp)

所以我的需求逐渐变得非常明确：

> 有没有一套统一框架，能够同时处理文档、博客、书籍、产品首页、发布下载和 API Reference，同时仍然坚持纯 Markdown、静态构建、本地优先和长期可维护？

没有完全符合我要求的。

那就自己做一个。

------------------------------------------------------------------------

### Oink 的答案：让内容保持干净，让框架承担复杂性

Oink 的设计并不是从一张设计稿开始的，而是从十几个真实网站里倒推出来的。

我先把这些网站实际需要的能力汇总起来，再尝试寻找一组尽可能简单、稳定和统一的抽象。

最终形成了几个非常明确的原则。

### 第一，原生 Markdown 优先

Oink 的第一原则不是“禁止组件”，而是：

> **能用原生 Markdown 表达的东西，就不要发明新的语法。**

比如步骤列表，本质上还是一个普通的有序列表，只需要在后面加上一行属性：

![图片](18.webp)

即使离开 Oink，这仍然是一份人类和其他 Markdown 工具都能读懂的有序列表。

文件树也是一样。很多框架会要求你写一大堆嵌套组件，而 Oink 使用一个接近纯文本的围栏块：

![图片](19.webp)

在 Oink 里，它会变成带文件图标、对齐注释、折叠目录和可拖动分隔线的文件树；离开 Oink，它仍然是一段一眼就能看懂的文本。

Callout 使用普通引用块，步骤使用普通列表，字段说明使用普通表格，图片、代码块和数据围栏也尽量沿用 Markdown 已有的语义。只有当原生 Markdown 确实无法表达某种能力时，才退回到 shortcode。

这不是洁癖，而是一条非常现实的工程原则：

**框架总会过时，内容应该活得比框架更久。**

------------------------------------------------------------------------

### 第二，只用 Hugo，别的都不要

Oink 使用 Hugo Extended 编译模板与样式。

正常构建不会从网络拉取字体、JavaScript 或第三方资源；浏览器侧需要的运行时也全部随主题一起交付。最终产物就是一个普通的 `public/` 静态目录。

-

<!-- -->

    hugo --gc --minify

构建完了，事情也就结束了。

你可以把它扔给：

•GitHub Pages；•Cloudflare Pages；•Netlify；•Nginx；•Caddy；•对象存储；•内网服务器；•离线安装包；•任何能托管静态文件的地方。

没有数据库，没有应用服务器，没有必须在线的 SaaS，没有某家搜索服务的账号，也没有部署后还得继续养着的 Node Runtime。

![图片](20.webp)

这就是 Oink 所说的 **Local-First**。

这里的“本地优先”并不只是“可以在 localhost 打开”，而是指整个系统的关键能力都掌握在你自己手里：

•内容在你的 Git 仓库里；•资源在你的构建产物里；•搜索索引在你的站点里；•构建过程可以复现；•网站可以离线运行；•托管平台随时可以更换。

文档尤其应该这样。

它不应该因为某个 CDN、外部字体服务、托管搜索或者前端依赖停止工作，就突然变成一堆打不开的壳。

------------------------------------------------------------------------

### 第三，一套框架，六种内容

真实项目很少只有“文档”。

一个成熟项目通常还会慢慢长出：

•产品首页；•博客与技术文章；•发布注记；•安装包与下载页；•API Reference；•有章节、图表和交叉引用的长篇书籍。

传统做法是为这些东西分别选择工具，然后努力让它们看起来像一家人。

Oink 反过来：先提供一套统一的页面外壳、导航、搜索、主题和输出体系，再在上面承载六类内容。

#### 文档

左侧目录树、右侧页内纲要、面包屑、上一篇/下一篇、编辑链接、历史链接、全文搜索和键盘导航都已经准备好。

![图片](21.webp)

![图片](22.webp)

#### \

#### 博客

支持作者档案、多作者署名、系列文章、标签分类、RSS、分享栏、列表/卡片/表格索引，以及适合长文阅读的沉浸式 Hero 页面。

![图片](23.webp)

![图片](24.webp)

#### 书籍

支持章节编号、图片与表格编号、公式、交叉引用、目录索引以及整本书连续打印。DDIA 这样的多语言复杂书籍，就是这套模型的主要试验场。

![图片](25.webp)

![图片](26.webp)

#### 发布与下载

一份结构化的 YAML 数据，可以生成发布卡片、下载资产列表、校验和与历史归档。发布状态不再散落在手写 HTML 里，而是成为可以检查和复用的数据。

![图片](27.webp)

#### Landing Page

Oink 内置了一组服务端渲染的首页区块，可以用数据和 Markdown 组合产品介绍、功能板块、指标、价格、FAQ、团队与行动入口。

你现在看到的 Oink 首页，以及 PGSTY 公司首页，本身就是用这套 Landing 系统搭出来的。

![图片](28.webp)

#### API Reference

Swagger UI 和 Redoc 以本地运行时的方式交付。OpenAPI 文件放在仓库里，即使在断网和内网环境中也能照常查看。

这六类内容并不是六套互不相干的主题。它们共享同一套设计语言、搜索、语言切换、版本选择、输出格式和组件体系。Oink 当前同时提供 21 类技术内容组件，并且只在页面实际使用某项能力时加载对应运行时。

这件事看似只是“少配几套主题”，真正带来的价值却很大：

**你终于可以用一套知识，维护整个项目的对外信息系统。**

------------------------------------------------------------------------

### 第四，功能可以很强，页面不能越来越重

Oink 里有不少高级组件：

![图片](29.webp)

但我的原则不是把所有 JavaScript 一股脑塞进每个页面里。

一个页面没有图表，就不应该加载 ECharts；没有 Mermaid，就不应该加载 Mermaid；没有终端录像，就不应该加载播放器。组件脚本根据当前页面实际使用的能力进行拼装，打印、Markdown 和 RSS 输出则完全不加载这些浏览器运行时。

这就是 Oink 所说的：

> **Content. On Demand.**
>
> 能力按需出现，负担不要到处乱跑。

更重要的是，每个组件都应该知道自己离开浏览器之后是什么。

在 HTML 里，它可以是一棵漂亮的交互文件树；在打印页面里，它应该完整展开；在 Markdown 输出里，它应该保留原始围栏；在 RSS 里，它至少应该退化成可阅读的源码。

这种“优雅降级”不是锦上添花，而是 Oink 内容模型的一部分。

因为一个真正可靠的文档组件，不能只在作者当前使用的浏览器和主题版本里成立。

------------------------------------------------------------------------

### 第五，为人类设计，也为 Agent 设计

Oink 对 AI Agent 的支持，不是在导航栏里随便加个 ChatGPT 图标就算完事。

它从输出层开始处理这个问题。

同一份 Markdown 内容，可以生成：

•HTML 页面；•打印页面；•原始 Markdown 页面；•RSS；•站点级 `llms.txt` 索引。

每个 HTML 页面都会声明自己对应的 Markdown 地址，Agent 和爬虫不需要先把导航、按钮、脚本与样式从 HTML 里剥掉，再猜哪一部分才是正文。

更关键的是，这个 Markdown 页面不是把 HTML 再反向转换一遍，而是尽可能保留你真正写下的内容。Callout、表格、文件树、代码围栏等组件也会按照各自定义的 Markdown 形态输出。

![图片](30.webp)

这意味着 Oink 站点不仅适合“被 AI 总结”，也适合成为 Agent 真正可操作的工程知识库。

你可以让 Agent：

•阅读站点结构；•找到特定配置；•获取当前页面的干净 Markdown；•根据已有文档继续补充内容；•检查中英文翻译；•更新发布注记；•直接在仓库里修改源文件。

在我看来，这是未来文档框架的基本能力，而不是一个可有可无的插件。

**浏览器只是文档的一个消费者，AI Agent 正在成为另一个。**

------------------------------------------------------------------------

### 搜索、多语言与版本，不需要再从头拼装

这些功能很少成为文档框架宣传页上最性感的部分，但在真实项目里，它们决定了一套系统到底能不能用。

Oink 的全文搜索完全在本地运行：Hugo 在构建时为每种语言生成 JSON 索引，浏览器下载后在本地搜索，不需要爬虫、账号、外部 CDN 或托管服务。拉丁文字使用 Lunr，中文、日文等 CJK 查询则有子串回退机制，不会出现英文能搜、中文形同虚设的尴尬。

搜索入口与命令面板合并，通过 `⌘ K` 或 `Ctrl K` 打开。除了搜文档，还可以搜索页面操作、语言切换与版本切换。

多语言直接使用 Hugo 原生模型，翻译文件与原文并排放置；英文、简体中文与繁体中文界面文本经过完整维护，页面之间可以建立稳定的语言对应关系。

多版本文档则提供版本菜单和旧版本归档提示，但不擅自规定你的部署方式。每个版本仍然是一个独立、可复现的 Hugo 构建，可以放在不同域名、子域名或者目录下。

除此之外，RSS、SEO、站点地图、Google Analytics、Giscus 评论、深浅色、打印样式、图片缩放、键盘导航、移动端布局和仓库编辑链接也都已经准备好了。

你当然可以自己把这些东西一点点拼起来。

但我做 Oink 的目的，正是让你不必再拼一次。

------------------------------------------------------------------------

### 先别听我吹，直接看十五个生产站点

目前 Oink 的公开案例库收录了 15 个真实站点，覆盖中英文大型文档、公司首页、开源项目、小型工具、聚合知识库和三本书；规模从只有两页的小站，一直到拥有上千份内容文件的发行版手册。

如果你准备做一个新站，不需要从空目录开始研究所有配置。直接找一个最接近的现成站点，照着抄就行。

![图片](31.webp)

这些站点不是专门为了截图做出来的 Demo，而是我自己每天都在使用和维护的生产站点。

这点很重要。

Oink 的不少设计不是来自“我觉得用户可能需要”，而是来自真实迁移过程中遇到的具体问题。设计文档所依据的一轮调查，覆盖了 11 个消费站点和五千多份 Markdown 文件；公开案例又进一步覆盖了从小工具到大型发行版文档的两端。

所以它不是一套想象中的通用方案，而是许多真实需求最终收敛出来的公共部分。

------------------------------------------------------------------------

### 它不是一张截图配一个 README

Hugo 主题很容易做出一个“看起来不错”的首页，然后在复杂内容、移动端、多语言、打印或者升级时原形毕露。

我不敢说 Oink 没有 Bug，但它不是一张截图配一个 README 的主题玩具。

0.6 版本除了真实站点验证，还覆盖了 HTML、打印、Markdown、RSS 与 LLMS 等 40 个黄金输出面，包含 85 项迁移测试、38 项浏览器运行时测试，以及双语站点构建、大型站点性能测量和真实中英文浏览器检查。

开发预览和生产发布也采用不同的错误策略。

普通 `hugo server` 遇到错误配置时，会尽量发出警告并安全降级，避免一个错别字让整个预览站点全部打不开；生产构建则使用 `--panicOnWarning`，让 CI 对警告保持严格，防止有问题的产物被发布出去。

这套思路很像数据库系统：

•开发阶段要尽可能提供诊断；•生产发布必须建立严格门槛；•错误不能悄悄制造误导性结果；•也不能因为一个局部输入问题，就让所有页面一起陪葬。

名字虽然叫 Oink，骨子里仍然是工程师做出来的东西。

------------------------------------------------------------------------

### 怎么开始？其实不用先学 Oink

这年头，说实话，我写这么多文档也不全是指望人逐页阅读的。

最简单的使用方式，是把任务直接交给 Codex 或 Claude Code。

Oink 的文档站本身就是完整样例和回归测试站，包含所有页面类型与组件。官方推荐的最快路径不是从空目录开始，而是把样例站克隆下来，删掉不需要的部分，再替换成自己的内容。

对人类来说，命令只有几行：

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

然后修改站点名称、域名和仓库地址，把 `content/` 换成自己的内容即可。

当然，你甚至不需要亲自做这些。

可以直接把下面这段话扔给 Agent：

![图片](32.webp)

你只需要负责两件事：

第一，告诉 Agent 你要做什么样的网站。

第二，把真正有价值的内容写出来。

剩下那些主题配置、目录组织、构建脚本和部署细节，本来就应该由工具处理，而不是让每个写文档的人重新学习一遍。

------------------------------------------------------------------------

### 谁适合使用 Oink？

Oink 最适合下面几类人。

#### 开源项目作者

项目已经写出来了，不想再花几天时间研究前端框架，只想尽快得到一套像样的首页、文档、博客和发布页。

#### 基础设施与工程软件团队

数据库、运维平台、中间件、开发工具、发行版和私有化软件，往往需要大量配置说明、命令、架构图、文件树、终端录像和版本文档。Oink 的组件与本地优先交付模式，就是围绕这类内容设计的。

#### 需要内网、离线或长期归档的团队

如果文档必须在私有网络、客户环境、离线安装包或隔离环境中运行，那么不依赖外部 CDN、搜索服务和运行时后端，会省掉很多麻烦。

#### 写书、做翻译和维护大型知识库的人

章节编号、交叉引用、图表公式、多语言、整书打印和稳定锚点，都是长篇内容真正会遇到的问题。

#### 同时维护多个项目的人

当你有五个、十个甚至更多项目时，统一框架的收益会迅速放大。你不再需要记住每个站点各自使用什么主题、什么组件方言、什么部署方式。

我自己把 18 个网站迁过去，就是最直接的例子。

------------------------------------------------------------------------

### 谁不适合使用 Oink？

Oink 不是万能框架，也没有必要装成万能框架。

如果你需要的是：

•拖拽式所见即所得 CMS；•在线多人协作编辑后台；•复杂的用户账号与权限系统；•大量动态业务状态；•一个可以随意嵌入 React 应用的前端平台；•依赖服务端实时计算的 Web 应用；

那么 Oink 不是正确选择。

它的边界很清楚：**以 Markdown 和结构化数据为源，构建静态、现代、可靠的内容网站。**

这个边界不是缺陷，而是它能够保持简单的原因。

------------------------------------------------------------------------

### 文档本来就应该是最不容易坏的东西

过去几年，我在文档这件事上确实折腾了不少。

用过 Docsy，拿 Hextra 放书，用 Blowfish 写博客，也试过各种前端框架。它们各有长处，但当网站越来越多、内容越来越复杂之后，我最终还是回到了最朴素的一条路：

•内容使用 Markdown；•构建使用单一二进制；•输出是普通静态文件；•所有资源尽可能本地交付；•样式和内容尽可能分离；•对人类和 Agent 使用同一份事实源。

这不是因为新技术不好，而是因为文档这种东西，真正重要的并不是技术栈有多时髦。

真正重要的是：

•十年之后还能不能打开；•换个平台还能不能部署；•离线之后还能不能使用；•人和机器还能不能读懂；•项目越来越大之后还能不能维护。

**文档本来应该是一个项目里最容易部署、最不容易坏的东西。**

Oink 只是试图把它变回本来该有的样子。

目前 Oink 已经以 Apache 2.0 协议开源，保留了 Docsy 的历史与上游署名，并对随主题分发的第三方运行时逐项记录许可信息。

它还远没有完工，但现在已经足够好，至少好到让我愿意把自己的十八个网站全部放在上面。

而只要这些网站还在，我就会继续维护它。

你不需要先研究一堆概念，也不需要从空白目录开始。

去案例库里找一个最像你需求的网站，复制下来，把内容换成自己的，然后交给 Agent 收拾剩余细节。

**把 Markdown 喂进去。****剩下的，交给这头猪。****![图片](33.webp)

**

------------------------------------------------------------------------

•Oink 文档与案例<sup>[1]</sup>•Oink GitHub 仓库<sup>[2]</sup>•Oink 文档站源码<sup>[3]</sup>

#### References

`[1]` Oink 文档与案例: *https://oink.pgsty.com/*\
`[2]` Oink GitHub 仓库: *https://github.com/pgsty/oink*\
`[3]` Oink 文档站源码: *https://github.com/pgsty/oink.pgsty.com*

数据库老司机*点一个关注 ⭐️，精彩不迷路*
