MkDocs Material 内置 offline 插件:构建无需服务器、可离线分发的文档站点
Material for MkDocs 是为数不多能够构建"离线可读"文档的静态站点框架之一——生成的文档无需 Web 服务器,用户解压后直接双击 index.html 即可浏览。本文围绕内置的 offline 插件,讲解其工作原理(如何让搜索在 file:// 协议下继续工作)、mkdocs.yml 中的最小配置与 enabled 参数、与隐私/优化插件的配合方式,以及启用离线构建时必须注意的功能限制,帮助你产出可直接以 .zip 形式分发的离线文档包。
Objective:为什么需要 offline 插件
工作原理:从 file:// 到可用交互
MkDocs 构建出的 site 目录本质上是一组纯静态的 HTML、CSS、JS 与资源文件。按照 构建站点 的流程完成 mkdocs build 后,切换到 site 目录并双击 index.html,文档即可在本地文件系统中直接打开,浏览器地址栏会显示 file:// 前缀。
但问题随之而来:Material for MkDocs 的许多交互功能依赖 Fetch API 发起网络请求,而现代浏览器出于安全考虑,禁止从 file:// 页面发起跨源请求,通常会抛出类似如下错误:
Cross origin requests are only supported for protocol schemes: http, [...]
站点搜索正是受影响最明显的功能——搜索索引 search/search_index.json 需要通过 Fetch API 获取,一旦走 file:// 协议便会失败,搜索框形同虚设。
offline 插件正是为解决这一问题而生的。它在构建阶段做了两件事:
- 把搜索索引内联为 JavaScript 文件:在
on_post_build钩子中,读取site_dir/search/search_index.json的内容,生成同目录下的search_index.js,其内容形如var __index = {...},让搜索数据不再依赖网络请求。 - 追加 iframe-worker 垫片(shim):通过
@squidfunk/iframe-worker项目,用隐藏 iframe 模拟 Web Worker 的异步能力,使 Lunr.js 搜索逻辑在file://协议下也能正常执行。
此外,插件在 on_config 钩子中自动关闭 use_directory_urls。目录式 URL(如 foo/bar/)依赖服务器重写才能正确解析,而本地文件系统没有这一能力;关闭后链接会退化为 foo/bar.html 形式,保证从本地直接打开时所有链接都能正确解析。这两项改动由 src/plugins/offline/plugin.py 中 OfflinePlugin 的 on_config 与 on_post_build 两个钩子完成,其中 on_post_build 被标注为 @event_priority(-100),确保其在构建流程的末尾、其他插件处理完成之后才执行。
何时使用
插件的定位非常明确:只在为离线分发构建站点时才启用它。也就是说,如果你正计划把整个 site 目录打包成 .zip 发给用户,offline 插件就是关键一环;而如果站点始终托管在服务器上,则没有必要启用。
离线场景下,offline 插件还与其他内置插件配合默契,能组合出更完整的离线体验:
- 内置 privacy 插件:离线构建时外部资源(字体、CDN 脚本等)无法访问,privacy 插件会在构建时自动把外部资源下载并内联进产物,让你的文档在完全断网的环境下也能正常渲染。
- 内置 optimize 插件:自动识别并压缩、转换站点引用的所有媒体文件,减小产物体积,让最终分发的
.zip更小、下载更快。
有人可能会问:为什么不让 offline 插件顺带实现外部资源下载?这是因为该能力已在 privacy 插件中完整实现,并成为其存在的核心理由。Material for MkDocs 的插件体系遵循模块化设计——各插件各司其职又相互增强,几个简单的配置即可解决复杂问题。
Configuration:在 mkdocs.yml 中启用
与所有 内置插件 一样,offline 插件的启用极其简单。在 mkdocs.yml 中添加如下配置即可:
plugins:
- offline
offline 插件随 Material for MkDocs 内置分发,无需单独安装。从源码结构看,它由 src/plugins/offline/config.py 定义配置项、src/plugins/offline/plugin.py 实现具体逻辑,二者对应发布在 material/plugins/offline/ 目录下。
General:enabled 参数
插件目前唯一的配置项是 enabled:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled |
布尔 | true |
是否在构建站点时启用离线处理 |
默认值为 true,即一旦在 plugins 中声明 offline,离线构建能力即生效。若希望一套配置同时产出在线与离线两种产物,可借助 环境变量 控制开关——例如默认关闭、仅当设置 OFFLINE 环境变量时才启用:
plugins:
- offline:
enabled: !ENV [OFFLINE, false]
使用该方式时,普通构建(mkdocs build)生成的是常规在线站点;而在设置 OFFLINE=true 的环境下构建,则得到可离线分发的产物。从 config.py 的 OfflineConfig 可以看到,enabled 通过 Type(bool, default = True) 定义,plugin 的两个钩子也都以 if not self.config.enabled: return 作为守卫,关闭时不会产生任何副作用。
Limitations:必须关闭的功能
浏览器的安全限制决定了并非所有交互功能都能在 file:// 下工作。启用 offline 插件后,以下依赖 Fetch API 的功能需要显式关闭,否则在本地打开时相关请求会报错:
- Instant loading(即时加载):点击链接时通过 Fetch API 预取并替换页面内容,
file://下无法跨源请求,需关闭navigation.instant。 - Site analytics(站点统计):统计脚本需要向后端上报数据,离线环境下不存在服务端,统计也就失去意义。
- Versioning(版本切换):版本选择依赖 Fetch API 动态获取版本列表,离线包中无法工作。
- Comment systems(评论系统):主流评论方案(如 Giscus、Gitalk)均需与外部服务通信,离线时同样不可用。
这些功能的具体开关方式可分别查阅上文链接的 setup 章节。关闭它们之后,你的离线文档将保留绝大部分核心交互——尤其是站点搜索——同时不会在本地打开时冒出红字报错。
源码视角:离线搜索的两条关键链路
深入源码可以看到,offline 插件与搜索模块的配合构成了完整的离线搜索链路:
索引加载的分流逻辑位于 src/templates/assets/javascripts/bundle.ts 的 fetchSearchIndex 函数:当 location.protocol === "file:" 时,通过 watchScript 动态加载插件生成的 search/search_index.js,并把全局变量 __index 作为索引数据;否则走常规路径,用 Fetch API 请求 search/search_index.json。这正是"移动搜索索引到 JavaScript 文件"这一设计的落点。
搜索 worker 的 iframe 垫片:搜索本身在 Web Worker 中执行以保持 UI 流畅,而 Web Worker 从 file:// 页面创建同样受限。搜索模块的 worker/_/index.ts 注释明确指出:借助一个轻量的、基于 iframe 的 Web Worker 垫片,搜索在 file:// 协议下也能得到支持。插件在 on_config 中向 config.extra["polyfills"] 追加的 https://unpkg.com/iframe-worker/shim(见 plugin.py)正是这个垫片。
此外,worker 内的多语言支持在 iframe 环境中需要修正脚本加载路径:worker/main/index.ts 通过检测 parent 上是否存在 IFrameWorker 来识别垫片环境,并依据首个带 src 的 script 元素重新推导 lunr 语言包的基础路径。
至此,离线站点保留的最重要交互——搜索——从索引内联、worker 垫片到链接重写,形成了一条完整且可验证的链路,这也是 offline 插件价值的核心所在。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0632
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00