1. 为什么我放弃了Sphinx和GitBook,转向Antora
先说个背景。我之前一直用Sphinx维护技术文档,后来接手了一个多产品线的项目,文档分散在四五个Git仓库里,每个仓库一套独立的文档站点,版本还各有各的标签。改一个跨产品的操作步骤,要在几个站点之间来回跳着确认,发布的时候更是噩梦——手动同步、版本对不上、目录结构各写各的。
后来我陆续试过GitBook、Docsify、VuePress,它们解决了一部分痛点,但都没法同时满足三个核心诉求:
- 多仓库内容聚合到一个站点
- 每个版本有独立访问路径,旧版本不消失
- 文档内容跟代码一起走,发布流程能自动化
直到我认真用了Antora,才发现这东西基本就是为这类场景设计的。它不是又一个“用Markdown生成静态站”的轮子,而是一个真正面向多仓库、多版本、组件化内容架构的文档站点生成器。
Antora的核心概念就三个:组件(Component)、版本(Version)、仓库(Repository)。理解这三个词,就理解了大半个Antora。
- 组件:一个逻辑上的文档单元,比如“用户手册”“API参考”,一个站点可以由多个组件组成。
- 版本:每个组件可以有多个版本,Antora会为每个版本生成独立的URL路径。
- 仓库:文档源文件所在的Git仓库,Antora聚合这些仓库的内容来生成站点。
如果你现在的处境和我当时类似——文档散落多仓、版本混乱、手工发布累到吐,那这篇文章值得看完。我会从设计思路讲到实际落地,再把我踩过的坑一并倒出来。
2. 先理解Antora的内容聚合方式:不是复制,而是映射
我第一次接触Antora时,脑子里还带着“把文件拷贝到某个目录里再生成”的惯性思维,结果理解上绕了不少弯路。后来搞明白了:Antora做的事情不是复制内容,而是通过配置文件把分散在各个Git仓库里的文档“映射”到一个统一的内容目录里。
这个映射关系定义在站点根目录的antora-playbook.yml中:
site: title: 我的产品文档中心 url: https://docs.example.com start_page: user-guide::index.adoc content: sources: - url: https://github.com/example/user-guide.git branches: [1.0, 2.0] start_path: docs - url: https://github.com/example/api-reference.git branches: [main] start_path: docs这段配置的意思很直白:
content.sources:声明要拉取哪些仓库,以及拉取哪些分支。每个分支对应组件的一个版本。start_path:仓库内哪个子目录是文档根目录。start_page:站点首页指向哪个组件、哪个版本的哪一页。
Antora拿到这份配置后,会做这几件事:
- 克隆每个仓库到本地缓存
- 根据分支名识别版本(比如
1.0分支就是1.0版本,main分支默认是未发布版本) - 解析每篇文档头和仓库内的
antora.yml组件描述文件 - 构建组件版本树,计算页面间导航
- 渲染并输出静态HTML站点
所以Antora实际是一个“文件收集器 + 文档解析器 + 站点渲染器”的组合。它不要求所有文档在一个目录里,而是在构建时动态聚合。也就是说,只要源仓库还在,重建站点随时能聚合出完整内容。
2.1 版本规则:分支名怎么变成访问路径
Antora对版本的处理很灵活,默认规则是你配置什么分支名,它就生成什么版本号。比如branches: [1.0, 2.0],访问路径就是:
https://docs.example.com/user-guide/1.0/ https://docs.example.com/user-guide/2.0/为了区分“已发布版本”和“最新开发版”,通常用main分支表示最新未发布内容。Antora会自动把main标记为pre-release版本,URL里会带上latest标识,但路径上不会出现“main”字样。
如果你希望某个版本在站点里显示为默认版本,可以这样配置:
content: sources: - url: https://github.com/example/user-guide.git branches: [1.0, 2.0] start_path: docs versioning: - version: 2.0 display_version: 最新版这样站点导航里“2.0”会被显示为“最新版”,但URL路径仍然是2.0。默认版本的选择规则是:版本号数字最大的、不是pre-release的那个。
2.2 组件描述文件:每份文档都要“自报家门”
每个文档源仓库里,都必须有一个antora.yml,它声明该仓库承载的是哪个组件。比如user-guide仓库的docs/antora.yml:
name: user-guide title: 用户指南 version: 2.0 start_page: index.adoc nav: - modules/ROOT/nav.adoc这里的name是组件标识,title是在站点导航栏里显示的名称,version会作为该组件此版本的版本号,start_page指定进入该组件后默认打开哪个页面。
如果你多个分支共用一份antora.yml,那版本号就会一样,Antora构建时可能报“版本冲突”之类的警告。我的建议是:每个分支的antora.yml里写对应的版本号,让版本信息和分支一一对应,避免混淆。
3. 目录结构:Antora的module体系,如何组织不同类别的内容
Antora沿用了AsciiDoc生态里的一套模块化目录约定。每个组件下可以拆成多个模块(module),每个模块按用途分为:
ROOT:该组件的默认模块,页面可以直接通过组件名::页面名.adoc引用tasks、concepts、references:按文档类型拆分,方便管理和权限控制
一个标准的组件目录结构是:
user-guide/ ├── antora.yml └── modules/ ├── ROOT/ │ ├── nav.adoc │ └── pages/ │ └── index.adoc ├── tasks/ │ ├── nav.adoc │ └── pages/ │ ├── install.adoc │ └── config.adoc └── references/ ├── nav.adoc └── pages/ └── parameters.adoc这种结构看起来层级多,其实好处是:不同用途的内容被物理隔开,不会混在一起。我写过一段时间的Sphinx,经常把“概念说明”和“操作步骤”揉在一个页面里,后期维护时想拆都费劲。Antora的module机制强制你做内容分类,从源头上逼着你把文档结构理清楚。
引用另一个模块的页面时,写法是:
详见 xref:tasks:install.adoc[安装指南]如果引用的页面就在当前模块里,可以直接写:
详见 xref:config.adoc[配置说明]引用其他组件的页面,需要加上组件名:
详见 xref:api-reference:references/endpoints.adoc[接口列表]这里的核心逻辑就是:启动Antora时,它会读取每个组件的antora.yml和模块结构,建立完整的引用索引。所以哪怕内容散落在不同仓库,最终站点的内部链接都是有效的。
3.1 start_page到底指到哪里
很多人初次配置start_page会写错。它指定的不是“文档源仓库里的路径”,而是构建后站点里的逻辑路径。
逻辑路径的语法是:组件名:模块名:页面文件名。
例如:
site: start_page: user-guide::index.adoc注意这里的::,它表示ROOT模块。如果你指定的是其他模块,就要写全:
site: start_page: user-guide:tasks:install.adoc刚上手时建议都用ROOT模块,少踩路径理解的坑。等站点结构稳定后再按需拆分模块。
4. 搭建一个Antora站点的完整流程
现在从头走一遍实际搭建流程。假设我们有两个仓库:
user-guide:用户指南,有两个版本分支(1.0、2.0)api-reference:接口文档,只有main分支
最终要生成一个包含这两个组件的站点。
4.1 安装Antora
Antora基于Node.js,我用的是npm全局安装:
npm install -g @antora/cli @antora/site-generator-default安装完成后可以确认一下版本:
antora --version如果不想全局安装,也可以把它作为项目的开发依赖放在package.json里,然后通过npx antora调用。团队协作时,我建议后者,能锁定版本避免环境差异。
4.2 准备文档源仓库
以user-guide仓库为例,克隆到本地后,在docs目录下创建antora.yml:
name: user-guide title: 用户指南 version: 2.0 start_page: index.adoc nav: - modules/ROOT/nav.adoc在docs/modules/ROOT/pages/index.adoc写首页内容:
= 用户指南 欢迎使用我们的产品。 参考 xref:tasks:install.adoc[安装说明] 开始上手。在docs/modules/tasks/pages/install.adoc写一个操作页:
= 安装说明 在终端中执行以下命令: [source,bash] ---- npm install -g @example/cli ----然后导航文件docs/modules/ROOT/nav.adoc里加上页面:
* xref:index.adoc[用户指南] * xref:tasks:install.adoc[安装说明]4.3 写Playbook并构建
站点根目录新建antora-playbook.yml:
site: title: 产品文档中心 start_page: user-guide::index.adoc url: https://docs.example.com content: sources: - url: ./user-guide branches: [2.0] start_path: docs - url: ./api-reference branches: [main] start_path: docs ui: bundle: url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip snapshot: true然后执行:
antora antora-playbook.yml如果一切正常,会生成build/site目录,里面就是完整静态站点。用浏览器打开build/site/index.html,就能看到合并后的文档首页。
4.4 多版本的配置方式
如果user-guide有1.0和2.0两个分支,Playbook就写成:
content: sources: - url: ./user-guide branches: [1.0, 2.0] start_path: docsAntora会自动读取每个分支里的antora.yml,把version字段写为2.0的分支归到2.0版本,写为1.0的分支归到1.0版本。两个版本会生成不同的URL路径:
https://docs.example.com/user-guide/1.0/index.html https://docs.example.com/user-guide/2.0/index.html站点右上角的版本切换器也会随之显示这两个版本。这样做的好处是:新旧版本文档同时在线,用户随时能切回旧版查看兼容性说明,而不用像以前那样维护两套独立站点。
5. Antora实战经验:我先后在三个项目上踩过的坑
Antora文档写得比较全,但有一些细节是文档里不太强调、实际用起来却很容易出问题的地方。
5.1 分支名和组件版本不一致
我早期在antora.yml里写死版本号1.0,但Playbook拉取的是main分支。结果构建出来的站点里,这个组件被识别为“main版本”,和预期完全对不上。
经验是:分支名决定Antora识别的版本来源,而antora.yml里的version字段决定最终展示的版本号。两者如果不一致,会有两种情况:
antora.yml里写版本,分支名不写版本:按antora.yml的版本展示- 分支名写版本,
antora.yml不写:按分支名展示
我自己的习惯是:发布分支用数字命名(如1.0、2.0),antora.yml里也同步写数字版本,从根上杜绝混乱。
5.2 nav.adoc的层级控制
Antora的导航层级完全靠nav.adoc里的嵌套列表控制。如果层级嵌套不当,可能出现导航里突然多出来一个子项、或者层级错位的问题。
一个常用的写法:
* 入门指南 ** xref:tasks:install.adoc[安装] ** xref:tasks:quickstart.adoc[快速开始] * 进阶主题 ** xref:concepts:architecture.adoc[架构说明] ** xref:references:parameters.adoc[参数参考]Antora会根据这个列表生成菜单结构,但注意:每一层的第一个条目会被作为标题处理,后面带链接的条目才是菜单项。这也是新手最容易搞混的点。
5.3 页面没有出现在导航里
有时候内容文件明明在pages目录下,但站点里找不到入口。原因基本都是:忘在对应模块的nav.adoc里添加条目。Antora不会自动扫描所有页面文件,它只渲染导航文件里明确引用到的页面。
我建议一开始就给每个模块维护一个简单的nav.adoc,每新增页面时顺手加进去,不要等文件多了再批量补。
5.4 外部链接和附件资源
Antora支持在页面里用link宏添加外部链接:
官方文档:link:https://developer.mozilla.org/[MDN]如果需要在页面里嵌入图片,把图片放到某个模块的images目录下,然后引用:
image::install-flow.png[安装流程]这里要求图片文件放在页面文件同级的images目录或模块根目录的images目录中,否则构建时找不到资源。
5.5 构建速度慢怎么办
Antora每次构建都会执行git clone或git pull。仓库很大、历史很长时,构建会明显变慢。可以启用镜像缓存,或者限制拉取深度:
content: sources: - url: ./user-guide branches: [1.0, 2.0] start_path: docs tags: []通过减少不必要的tag拉取来减轻负担。如果仓库本身就慢,更好的做法是先手动git clone到统一目录,Playbook里指向本地路径。
6. 发布策略和自动化:把Antora接进CI/CD
Antora产出的是纯静态文件,部署非常灵活。可以扔到Nginx、S3、GitHub Pages、腾讯云COS等任意静态资源托管平台。我的推荐是把它放进CI流程里,实现“push标签即发布”。
6.1 一个最简的GitHub Actions示例
name: Build Docs on: push: branches: - main tags: - v* jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm install -g @antora/cli @antora/site-generator-default - run: antora antora-playbook.yml - uses: actions/upload-pages-artifact@v3 with: path: build/site这个流水线做的事情很简单:代码更新或打标签时自动构建文档,然后发布到GitHub Pages。如果你用的是别的CI平台,思路也是一样的——无非是先拉源码、再构建、再上传产物。
6.2 多仓库之间的发布顺序
多仓库场景下,A仓库的文档更新后,可能要等B仓库也更新,才能生成一份完整的站点。这时候可以把每个仓库的docs目录都作为独立资源,由统一的Playbook来拉取和聚合,而不是每个仓库各自维护一套发布脚本。
我在实际项目里是把Playbook放在一个单独的“文档发布仓库”,其他仓库只维护内容。发布仓库的CI负责拉取其他仓库的最新分支,重新生成站点。这样内容所有权清晰,发布入口唯一。
7. 定场UI主题和品牌定制:别用默认UI糊弄用户
Antora默认的UI主题是Antora官方提供的基础样式,功能齐全但识别度不高。如果文档站是给客户或团队外部看的,我建议花点时间定制。
UI定制有两条路:
- 基于默认UI包修改CSS变量
- 基于UI源码二次开发,替换布局和组件
如果你只是想要品牌主色调、Logo、页脚信息,改动量不大。Antora官方UI仓库是antora/antora-ui-default,可以Fork后改代码,再构建成zip包,Playbook里指向自己的zip地址:
ui: bundle: url: https://example.com/custom-antora-ui.zip snapshot: true有一家公司在内部用Antora做产品文档中心,只调整了颜色变量和顶栏Logo,看起来就和默认主题完全不一样了。
7.1 定制时的几个关键点
- 默认UI包自带搜索功能,基于
lunr或pagefind,如果你建设的是公开站点,建议确认搜索索引是否能够覆盖所有版本 - 导航组件默认支持折叠,但如果你的导航层级很深,建议在使用前先在测试站点里检查折叠交互是否顺畅
- 自定义
site.url会影响站内搜索和SEO,生产环境务必绑定正式域名
8. 和其他文档生成器的对比:为什么最后留的是Antora
我用过的方案里,Sphinx最强的是Python生态的自动文档提取(尤其配合autodoc),GitBook编辑体验好但开源自托管版早已停止维护,VuePress/ Docsify适合轻量个人站点,团队级多版本聚合场景还是Antora更牢靠。
一个简单的对比表:
| 工具 | 多仓库聚合 | 多版本发布 | 内容来源 | 技术栈 |
|---|---|---|---|---|
| Antora | 原生支持 | 原生支持 | Git仓库 | Node.js |
| Sphinx | 需扩展配置 | 需扩展配置 | 本地目录 | Python |
| GitBook | 较弱 | 较弱 | Git仓库 | Node.js |
| VuePress | 需插件 | 需插件 | 本地目录 | Node.js |
| Docsify | 不支持 | 不支持 | 本地目录 | Node.js |
如果你的核心诉求是“组件的多版本聚合、内容跟着代码仓库走、构建结果可直接部署”,Antora的整合度最高。
但也要说清楚:Antora的文档源格式要求是AsciiDoc,不是Markdown。很多人会因为这一点犹豫。其实AsciiDoc的学习成本不算高,常见标题、列表、表格、代码块语法和Markdown差异不大。而且它天生支持交叉引用、条件内容、术语表,这些恰好是复杂文档需要的特性。
9. 进阶技巧:组件版本混合,如何展示“最新”和“历史版本”
多版本并存时,Antora站点会展示一个版本选择器。但有时候业务上希望“最新版”和“历史维护版”混在一起展示。这里有一个设置值得关注:
content: sources: - url: ./user-guide branches: [1.0, 2.0] start_path: docs versioning: - version: 2.0 display_version: 当前版本 - version: 1.0 display_version: 旧版本同时,可以把历史版本标记为prerelease,这样它不会成为默认展示版本,但依然可访问:
content: sources: - url: ./user-guide branches: [2.0] start_path: docs - url: ./user-guide branches: [1.0] start_path: docs versioning: - version: 1.0 prerelease: true这样配置以后,1.0还是可以在URL里直接访问,但不会干扰默认体验。对于需要长期维护多个版本的B端产品,这种模式特别实用。
10. 一个成熟的最小站点配置参考
最后放一个我可以直接套用的最小配置,适合刚导团队转到Antora时使用。
project-docs/ ├── antora-playbook.yml └── src/ └── user-guide/ ├── antora.yml └── modules/ ├── ROOT/ │ ├── nav.adoc │ └── pages/ │ └── index.adoc └── tasks/ ├── nav.adoc └── pages/ └── install.adocPlaybook内容是:
site: title: 团队文档中心 start_page: user-guide::index.adoc url: http://localhost:8080 content: sources: - url: ./src/user-guide branches: HEAD start_path: . ui: bundle: url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip snapshot: true执行构建:
antora antora-playbook.yml然后:
cd build/site python3 -m http.server 8080打开浏览器访问http://localhost:8080看效果。这套流程从搭建到预览只需几分钟,适合团队内部先跑通,再决定是否接入CI。
我在实际使用中发现,Antora最容易被低估的一点就是它“仓库复用”思想:同一个组件仓库,不同的分支天然对应不同版本,内容维护和代码发布可以走同一条流水线。这一点在真正跑起来之后,省掉的是大量重复的文档管理动作。如果你正在纠结该选什么文档工具,又恰好同时有多仓库和多版本的痛点,Antora值得你认真试一次。