python mkdocs
# Python MkDocs:一个文档工具的真实面貌
大概是在三年前,我接手了一个已经开发了两年的内部框架,代码写得相当漂亮,但文档散落在十几个Word文档里,版本号混乱得让人抓狂。那是我第一次认真琢磨MkDocs,后来发现,这工具在文档生成这条路上,走了一条挺聪明的路。
它到底是什么
很多人第一次听说MkDocs,会以为它是个类似Sphinx的庞然大物。实际上它很简单——一个把Markdown文件转成静态网站的工具。核心逻辑就是把一堆.md文件,按照你指定的目录结构,生成一套可以直接扔到web服务器上的HTML页面。
举个不太恰当的例子,就像你把笔记本上写好的笔记,按原样打印出来装订成册——MkDocs就是那个打印机和装订机的组合体。它不会帮你重新组织内容,不会自动生成流程图,也不会从代码里提取文档字符串。它就是个忠实的格式转换器,附带一个还算好用的导航系统。
设计哲学也很直接:你写Markdown,它生成网站。没有数据库,没有后台,没有动态内容。生成的每张页面都是静态HTML,这意味着你可以把它托管在GitHub Pages、Netlify或者任何一个支持静态文件的服务器上。这点和Sphinx那种“要把docstring抽出来、把Python模块映射成文档”的思路完全不同。
它能干什么
最基础的功能是把Markdown转成HTML,提供一套默认主题,让你点几个按钮就生成一个看起来还不错的文档站点。但我猜你不太在意这个,真正让它变得有价值的是另外几件事。
主题系统可能是它最讨巧的设计。默认的mkdocs主题很朴素,但Material for MkDocs这个第三方主题几乎成了事实标准——搜索、代码高亮、版本切换、多语言支持,全都有。有个细节:Material主题的搜索是预先生成索引的,不像有些方案需要依赖Elasticsearch或者浏览器端解析,这就让整个站点的响应速度很快。曾经测试过一个包含500多页的文档集,搜索响应时间基本在100毫秒内。
插件机制是第二个亮点。你可能需要自动生成API文档?有插件。想把注释转换成可视化图表?有插件。需要在构建前对文件做些预处理?也有插件。不过有一点值得注意——插件质量参差不齐。遇到过几个声称能“一键生成美观文档”的插件,实际上只是把代码高亮搞得更花哨了些。
还有个容易被忽略的功能:它是增量构建的。编辑了一个Markdown文件,MkDocs只会重新生成对应的HTML,而不是整个站点。这在文档比较大的时候能节省不少时间。曾经维护过一个2000多页的文档库,全量构建需要大约45秒,但增量构建只需要2-3秒。
怎么使用
安装特别简单,一行命令就行:
pipinstallmkdocs然后是初始化:
mkdocs new my-projectcdmy-project这会生成一个docs目录和一个mkdocs.yml配置文件。默认的docs目录下已经有个index.md,你可以把它理解成首页。
编辑mkdocs.yml,告诉它你的文档结构:
site_name:我的文档nav:-首页:index.md-入门指南:getting-started.md-API参考:-核心模块:api/core.md-工具函数:api/utils.md然后用mkdocs serve启动本地开发服务器,浏览器打开http://127.0.0.1:8000就能看到效果。每次保存文件,页面会自动刷新。
当你觉得差不多了,运行mkdocs build,所有静态文件会生成在site目录下。把这个目录丢到服务器上就行。
有个小技巧:如果你想让站点看起来更专业,可以搭配Material主题:
pipinstallmkdocs-material然后在mkdocs.yml里修改主题:
theme:name:material主题配置起来有点东西。比如如果你想自定义颜色,直接抄Material的色板就行:
theme:palette:primary:indigoaccent:deep orange最佳实践
这几年踩过不少坑,说几个实实在在的建议。
关于目录结构:不要学Sphinx那种“把文档和代码放在同一个仓库”的做法。MkDocs最好独立成一个单独的文档仓库,尤其是当你的项目已经有复杂的CI/CD流水线时。曾经因为把文档和代码混在一个仓库里,每次代码更新都要重新构建文档,结果就是文档构建时间从不到1分钟变成了17分钟。分开之后,文档仓库的CI只负责文档,清爽得很。
关于导航:导航结构最好保持扁平,嵌套不要超过三层。超过三层,用户找东西就变得像在翻一个没目录的文件夹。另一个问题:如果导航项超过15个,用户通常会失去耐心。一个解决办法是把不常用的内容放到“附录”或者“参考”部分,用标签页或者下拉菜单收纳起来。
关于版本管理:MKDocs本身不提供版本控制,但Material主题有个版本切换功能。做法是在site目录下放一个versions.json文件,指定不同版本的路径。生产环境里,可以把这个和CI结合——每次发布新版本的时候,自动把旧版本的文档归档,然后添加新版本。这样用户就能随时切回旧版本文档,而不必去Git仓库翻历史。
关于图片和资源:这是一个常见的陷阱。很多开发者直接把图片放到docs目录下,但MkDocs在处理图片路径时有点特别。一是图片路径要相对于docs目录,而不是相对于当前Markdown文件的路径。二是个坑:如果你在多个页面引用同一张图片,最好把图片放在一个独立的assets或img目录里,不然更新图片时,所有引用它的文件都要改路径。
关于自定义CSS/JS:可以通过extra_css和extra_javascript引入外部文件。一个常见的需求是添加自定义的代码块样式,比如给不同语言的代码块加上不同的边框颜色。做法是在docs目录下创建css/custom.css,然后在mkdocs.yml里配置:
extra_css:-css/custom.css同类技术对比
这个领域其实竞争者不多,但各有各的调性。
Sphinx:如果要说MkDocs有什么天然的对手,那就是Sphinx。Sphinx更适合那种需要从Python源码自动生成API文档的场景——它会解析你的rst文件,也会从模块的docstring里抽内容。MkDocs没有这个能力,你需要在Markdown里手动写API文档。但Sphinx的缺点是太重了:安装Sphinx及其依赖,文件体积大概在50MB左右;而MkDocs加上Material主题也才20MB出头。Sphinx的学习曲线也陡峭——reStructuredText语法本身就有不少特殊规则,配置起来也颇为复杂。
GitBook:一度很流行,但现在基本被放弃了维护。GitBook的编辑器挺好用,但生成的文件结构复杂,严重依赖他们的云服务。如果哪天GitBook公司倒闭,你自己生成的文件可能就打不开了——这不是危言耸听,早年用GitBook的几个开源项目已经遇到了这个问题。MkDocs生成的是纯静态HTML,不存在这个风险。
Docsify:Docsify是个比较文艺的选择——它不需要构建步骤,直接读取Markdown文件,在浏览器端渲染。这意味着你只需要一个简单的HTTP服务器就能运行文档。但它的缺点很明显:首屏加载慢,SEO不友好(搜索引擎爬虫抓不到内容),而且需要用户端有JavaScript支持。MkDocs是静态生成HTML,即使关闭JavaScript也能正常浏览。
Docusaurus:这是Facebook出的工具,功能和MkDocs类似,但用React构建。Docusaurus支持版本管理、i18n、搜索,功能很全。但如果你团队里没有React经验,学习和维护成本会比MkDocs高。MkDocs用Python和Markdown,这对Python开发者来说几乎是零学习成本。
有个有意思的观察:如果你的文档页数少于50页,或者团队里主要是Python开发者,用MkDocs会比较顺手。如果你的文档超过200页,或者需要从源码自动生成大量API文档,Sphinx可能是更好的选择。而如果你的文档主要受众是前端开发者,Docusaurus的体验会更好。
回到开头说的那个内部框架,最后用MkDocs重建了文档,配合Material主题和几个插件,效果还不错。当然也有遗憾——有些从Sphinx迁移过来的项目,初期总有人抱怨找不到API文档,因为MkDocs不支持自动抽取docstring。后来想了个折中方案:用mkdocstrings插件,配合sphinx-style的注释格式,勉强实现了类似的效果,但总归不如Sphinx来得纯粹。
这大概就是MkDocs的定位——它不强求做所有事情,但把文档生成这件事做到足够好用。你不需要是个全栈工程师,不需要懂React或者RST语法,只需要会写Markdown,就能生成一套能见人的文档。对于大多数Python项目来说,这已经足够了。
