2025 年 06 月 25 日

维护开源软件的网站


文档对开源软件的重要性毋庸置疑,毕竟用户很少会对一上来就阅读源码感兴趣。一些简单软件可以只使用 README,但更多的软件需要上百页相互链接的文档来展现自己的功能。为了更好地展示这些文档,维护者们往往会选择创建一个网站。

此时,文档的维护变成了两件事:

  1. 维护内容;
  2. 维护网站;

内容当然是很重要的;而维护网站,同样重要。维护不当,网站会变成项目的累赘。

一个例子

以本人参与维护的 https://apisix.apache.org faviconApache APISIX 为例,整个项目的文档分散在 https://github.com/apache/apisix faviconAPISIX 主仓库https://github.com/apache/apisix-docker faviconAPISIX Dockerhttps://github.com/apache/apisix-dashboard faviconAPISIX Dashboardhttps://github.com/apache/apisix-ingress-controller faviconAPISIX Ingress Controller,以及 https://github.com/apache/apisix-website faviconAPISIX Website 等多个仓库中。

屏幕前的各位也许觉着一般。但对本人这种没怎么接触开源项目的菜鸟而言,软件和文档的复杂度已经相当高了。

由于早期并未参与网站建设,待本人接手时,网站已经遇到了不少问题,比如:

  • 网站框架陈旧,缺少一些文档维护者需求的功能(Mermaid、图片懒加载等)。
  • 网站会构建所有子项目、所有版本的文档,导致构建时间过长。
  • 网站首页加载过多内容,非常缓慢。
  • 网站的博客部分展示效果与文档相同,极其丑陋。
  • 网站框架与文档内容分离,导致文档维护者无法及时预览文档的渲染效果。

目前诸位能看到的网站,是重构后的版本。受限时间和精力,当时只实现了重写博客、减少首页加载内容、改进构建方式。由于变更不够彻底,加上没能在更换框架方面说服项目主要维护者,许多问题依然存在。时至今日,有些问题已经变得难以接受,亟待改进。

从 2021 年底到现在,本人对该类问题有了些许思考,写成此篇文字,权当抛砖引玉。如能对各位有所帮助,那就太棒了。

选择

选择网站框架不是个难事。生态繁荣、持续更新、文档清晰、社区活跃的框架,很难遇到底子上的问题。唯需理解的是,也许某些网站框架能在初期快速满足一些需求,后续的定制化、维护升级成本却很高。

注意,本文希望谈论的是 SSG,即静态网站生成器。使用 CMS 似乎不是开源项目常见的选择,不仅不方便项目的维护者协作,还可能带来安全问题(比如 WordPress)。至于 SSR,目前没有找到采取这种方案的必要性。

Docusaurus

如果各位对 React 比较熟悉,想必听过同为 Facebook 出品的 Docusaurus。这也是 Apache APISIX 在使用的网站框架。

对于一个文档内容不多,没有任何定制化需求,最好是滚动更新没有版本化的网站来说,Docusaurus 用起来没问题。

然而一旦文档数量激增或需要深度定制,Docusaurus 的缺点便会暴露无遗。

Docusaurus 在构建时会把所有内容加载进内存,对于文档量巨大的项目而言,这无疑是一场灾难。早期 Apache APISIX 网站的构建时间甚至长达一小时。

想渲染一些特殊的内容,比如用 https://github.com/rohit-gohri/redocusaurus faviconRedocusaurus 来渲染 OpenAPI 文档,不止构建时间难以接受,占用的内存也是可怕得很。在参与过的项目中,出现过渲染几个稍大一些的 OpenAPI 文档(1~2MB),就让 4C8G 的机器(Vercel Pro 套餐)直接 OOM 的情况。这自然说明了 Redocusaurus 的实现存在问题,但与 Docusaurus 将内容都加载到内存中的特性脱不开干系。

Docusaurus 虽然使用 React 开发,在主题定制方面却异常不灵活,必须使用一个名为 swizzle 的特性。而涉及到 swizzle,一旦有升级的打算,大量修改是免不了的。

Apache APISIX 的网站停留在 2.0 beta 版本,原因正是在 2.0 beta 版本后、正式版本前,Docusaurus 对 swizzle 相关的功能进行了大刀阔斧的修改。Docusaurus 的升级文档也并不清晰,导致后续很难在较短的时间内完成升级工作。当然,使用 beta 版本是那时对 Docusaurus 太有信心了,以为能和 React 一样,版本间提供良好的兼容性。

此外,在某个本人参与过的项目中,使用了 Docusaurus 2.4.x 版本,由于开启了 swizzle 特性,导致在升级到 3.8.x 版本时,需要进行增删了各 6K 行的修改。因为某些选项的调整,后续又提交了多个 PR 以修复没有在升级指引中提及的修改。──可见至今,Docusaurus 的升级指引仍不完善,swizzle 也与破坏性变更常伴。

总之,即使各位维护的软件还在早期阶段,只要能预测到未来文档会变得很多,不想构建速度太慢,或者有很多定制化需求,请不要选择 Docusaurus。

想踩坑,请先看看 Docusaurus 的 Issues,包括关闭的,有些 Issue Open 的时间可不止一两年。

此外,作为一个主要由前端开发者打造的框架,Docusaurus 的默认审美也实在有些……难以言说。

Hugo

如果项目有非常多(1k+)的文档,Hugo 是个不错的选择。Hugo 是 go 语言开发的。

Hugo 的好处是可以在不怎么修改网站框架的情况下,实现很多定制化需求,并更晚遇到性能问题。

坏处是专精 JS 的网站维护者,可能对 go 语言不够熟悉,导致在某些功能上,难以上手或产生排斥。(不过现在有 AI,简单的语法、技巧难成问题。)

请放松,Hugo 的文档和设计不错。自定义程度相当高,能满足大部分需求。只要不是已经深陷 Next.js 或类似框架的开发者,应该不难用起来。

理论上说,使用 Hugo 的大部分时间只是和 template 打交道,该由 css 和 js 实现的功能,还是需要使用前端的工具链。

VitePress,Astro 等基于 Vite 的框架

不知不觉,Vite 的使用量已经相当高了。不管是 Vue,Svelte,Solid,甚至是 React,都能使用 Vite 来构建。基于 Vite 的网站框架,也相当多。愿意只用 Vue 的,可以尝试 VitePress;想不被任何库绑架的,可以尝试 Astro。还有其他不少网站框架,这里没法一一列举了。

这些框架基于 Vite 构建,在生态上,他们能共享很多东西。除了某些库限定的功能,大概能找到任何想要的东西。

至于构建速度,大体比 Hugo 要慢不少,比 Docusaurus 要快一些。

注意,像 VitePress 没有内置 Docusaurus 中所谓的 Blog 特性,但本人不认为这是一个缺点,详细请继续看下文。

Gatsby 和其他不再更新的框架

本人参与过 OI-Wiki 网站的 Next 版本开发,当时采用的是 Gatsby。Gatsby 的设计非常不错;如果想自己做一个「内容提供层」,推荐去参考一下。不知为什么,没有流行起来,也已经很久没有更新了。

从维护角度看,请不要使用任何缺乏更新的框架。除非准备好完全自己来维护了。

结构

子项目中的文档

如果维护的内容在同一仓库中,遇上的问题都会少很多。

麻烦的是像 Apache APISIX 这样,内容分散在多个仓库中的。

本人对这种分散没有意见。反之,将多个子项目的内容放在同一仓库中,更新完子项目功能还需要在另一仓库中创建 PR、更新文档,在流程上存在割裂。

对于子项目中的文档,约定好统一的结构即可。比如:

- docs/
  - latest/
    - en/
      - index.md
    - zh/
      - index.md
  - v3.0/
    - en/
      - index.mdx
    - zh/
      - index.mdx

网站仓库

对于承载网站本身和剩余其他内容的仓库,推荐使用 monorepo。

且最重要的一点是:拆!

已经有非常重历史包袱的项目,拆起来费时费力,除了考虑兼容历史遗留,还需考虑其他维护者在 review 代码时的感受。所以对于新项目,请务必考虑拆分,应拆尽拆。

https://github.com/apache/apisix-website faviconAPISIX Website 为例,更好的做法是:

  • 将 blog、docs 和其他页面的 代码部分 分别拆到 apps/ 的各自文件夹中。
  • 将 blog 和 docs 的 内容部分 拆到 content/ 的各自文件夹中。(这里的 docs 是指剩余的独立文档,不包括子项目中的文档。)
  • 对于共用的组件(header、footer)等,拆到 pkgs/components 文件夹中。

最后看起来像这样:

- apps/
  - blog/
  - docs/
  - website/
- pkgs/
  - components/
- content/
  - blog/
  - docs/

拆开后,所有的部分都可以单独维护。对依赖的升级,完全能分开进行。某天想拆到另一个仓库,也不是问题。

这也是为什么本人认为其他框架没有内置 Docusaurus 中 Blog 特性,是一个缺点。博客本身就跟文档关系不大,而其他专门的 Blog 框架也大概率做的比 Docusaurus 的 Blog 要好得多。

还有一个额外的好处是,文档部分的代码和内容完全分离,其他子项目可用 git submodule 等方式引 apps/docs 构建预览页面,非常方便。(APISIX Website 现存的处理方式做这件事相对困难,会构建太多不需要的东西。)

构建

Docusaurus 有一个官方推荐的实践:将旧版本的文档 archive 到另一个子域名中。此时,存在一些问题:

  • 旧版本如何提供版本切换按钮,看到最新的文档链接?
  • 旧版本如何提供一个 Banner,告知用户最新的 release 版本(非 latest)链接?
  • 旧版本文档更新(patch)时,如何更新该版本旧文档?
  • 何时存档旧版本文档?如何在主站添加这些旧文档的链接?

这些问题,有的可以通过脚本自动或半自动化,有的则需要手动处理。手动处理也不难,但在开源项目中,如果能减少维护,自然是更好的。

另外本人更倾向于将所有能暴露的东西都暴露出来,像 archive 到子域名这件事,非 maintainer 肯定是操作不了了。一定程度上阻碍了潜在的维护者参与。

而基于前文的「结构」一节,可以做一件很合理的事情:将每个子项目每个版本的文档分开构建,生成到 /{project}/{version} 下。

再加一些细节,就能解决上述这几个问题:

  • 版本切换的下拉框,我们可以用一个 JSON 文件维护,组件放到 Header 中。
  • Banner 处理方式同上。
  • 旧版本更新可以自动处理,在构建时根据 git commit SHA 来判断是否需要构建即可。
  • 旧版本一直在构建缓存中,已经没有这个问题了。假使历史文档太多,缓存都存不下,可以回到 Docusaurus 的方案,将旧版本文档 archive 到另一个子域名中。

如果已经采取了 Docusaurus 推荐的措施,即使像本文说的那样进行了改造,也仍然可以不处理已经 archive 的旧版本文档。 (能轻装上阵,为什么不呢?)

具体的构建实现,难度不高,不细说了。

总结

文中提出的思路,是基于本人参与维护的 Apache APISIX 网站的实践。每个开源项目有自己的情况,请根据实际情况参考。

(没错,为了吐槽 Docusaurus 才写的这篇。😅)

文章标题 维护开源软件的网站
文章作者 Young
发布时间 2025-06-25

转载或引用本文时请遵守 CC BY-NC-SA 4.0 ,注明出处,不得用于商业用途。
Loading comments...