Slidev 离线部署实战:开启 pwa 预缓存,让整份演示文稿在无网络时也能完整播放
【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev
如果你需要在陌生或受限的机器上演讲、在信号不稳的会场展示幻灯片,或者干脆想把一份自托管的演示打包成"断网也能跑"的应用,那么 Slidev 的pwa选项正是为此设计的:它通过slidev build生成一个基于 Service Worker 的渐进式 Web 应用(PWA),把整份演示文稿的所有资源预先缓存下来。读完本文,你将掌握pwa选项的完整配置语义、vite-plugin-pwa的按需安装流程、预缓存的底层实现原理,以及大媒体文件、远程资源等边界情况的处理方案。
本功能对应的官方文档见 docs/features/pwa.md,自 v52.17.0 起提供,属于可独立使用的可选特性之一(可在 特性索引 中浏览全部特性)。
为什么幻灯片需要"离线优先"
一次演讲可能发生在各种不可控的环境中:主办方提供的笔记本权限受限、会议现场 Wi-Fi 时断时续、甚至根本没有网络。普通 Web 版幻灯片在加载后会按需请求图片、视频等资源,一旦中途断网,后续页面就可能加载失败。
Slidev 的解法很直接:以opt-in的方式开启pwa后,slidev build构建出的产物会携带一个 Service Worker(由vite-plugin-pwa和 Workbox 驱动),它会把每张幻灯片的全部资源都预缓存(precache)——包括 JavaScript、CSS、HTML,以及所有图片、视频和音频。只要演示文稿被成功访问过一次,之后的整场演讲就完全从缓存中读取,不再有逐页拉取资源、中途卡壳的风险。
快速上手:在 headmatter 中开启 pwa
pwa是一个面向整份演示的配置项,只需在第一张幻灯片的 frontmatter(即 headmatter,见 headmatter 配置)中开启即可:
--- pwa: true ---pwa选项的取值既可以是布尔值,也可以是字符串,用来精细控制 Service Worker 在哪个阶段生效:
| 取值 | 行为 |
|---|---|
false(默认) | 不生成 Service Worker,完全关闭 |
true | 开发服务器和构建产物中均启用 |
'build' | 仅构建产物启用(离线部署最常用的组合) |
'dev' | 仅在开发服务器中启用 |
从类型定义可以印证上述语义:在 packages/types/src/frontmatter.ts 中,该字段被声明为pwa?: boolean | 'dev' | 'build'。而具体的"模式匹配"逻辑位于 packages/slidev/node/vite/pwa.ts:matchMode只有在配置值等于true、或与当前构建模式(dev/build)字符串相等时才判定为启用。
由于预缓存全部资源开销较大,pwa默认是关闭的(配置参考文档 docs/custom/index.md 中同样标注默认值为false),需要使用者有意开启。它最有价值的应用场景是与 部署与托管 中介绍的slidev build配合:构建并自托管一份希望能在离线状态下正常播放的演示。
安装 vite-plugin-pwa:可选依赖的自动安装与手动安装
PWA 能力由vite-plugin-pwa提供,但它被设计为可选的 peer dependency(optional peer dependency)——Slidev 默认不安装它,因此未开启pwa的演示项目永远不会下载这个包。
当你第一次在配置里把pwa设为启用值并运行命令时,Slidev CLI 会检测到该包缺失,并弹出交互式提示请你确认安装:
? The "pwa" option requires the "vite-plugin-pwa" package, which is not installed in your project. Install it now? › (Y/n)确认提示后,Slidev 会使用你项目当前的包管理器自动安装它(如果 Slidev 本身是全局安装的,则会安装到全局环境中)。这一按需解析与提示安装的完整流程实现在 packages/slidev/node/vite/pwa.ts 的resolveVitePWA函数中:先通过importOptionalDependency尝试懒加载包,失败则调用promptForOptionalInstallation发起安装提示并重新加载;若最终仍未获得VitePWA工厂函数,则抛出明确的错误信息。
需要注意:在非交互式环境(如 CI)中无法弹出提示框,此时应提前手动安装依赖:
npm i -D vite-plugin-pwa与之对应,当pwa处于关闭状态时,createPWAPlugin 会返回一个极简的slidev:pwa-register-stub占位插件,把客户端用到的virtual:pwa-register虚拟模块解析为一个空操作(见 createPWARegisterStubPlugin),保证开发服务器中受保护的动态import仍可解析,同时完全不需要拉取vite-plugin-pwa依赖。
工作原理:从构建产物到完全离线的缓存链路
构建期:Vite 插件与编译期特性开关
在 packages/slidev/node/vite/index.ts 中,createPWAPlugin(options)被加入到 Slidev 的 Vite 插件列表。同时,packages/slidev/node/options.ts 会把启用状态编码进编译期常量__SLIDEV_FEATURE_PWA__,其值由matchMode(config.pwa)决定。
这个编译期开关是"无成本运行"的关键:客户端代码中所有相关分支都受__SLIDEV_FEATURE_PWA__保护(例如 App.vue 中v-if="__SLIDEV_FEATURE_PWA__"的条件渲染、setup/main.ts 的按需初始化)。当pwa关闭时,这部分代码会在打包时被tree-shaking 摇掉——官方文档明确承诺:关闭状态下插件是彻底的 no-op,注册逻辑与右下角状态指示器都不进入最终包体,运行时零开销。
插件配置:Workbox 与 Manifest 的默认值
真正启用时,Slidev 以如下配置调用VitePWA(见 packages/slidev/node/vite/pwa.ts),这些默认值直接决定了"离线可用"的实际边界:
registerType: 'autoUpdate':Service Worker 采取自动更新策略,避免演讲中途使用过期缓存;injectRegister: null:插件本身不注入注册脚本,改由 Slidev 客户端逻辑自行注册(见下文);workbox.maximumFileSizeToCacheInBytes: 100 * 1024 * 1024:把 Workbox 预缓存的单文件大小上限从默认值提升到100 MB,避免大视频等文件被静默跳过;workbox.globPatterns:预缓存 glob 覆盖js/css/html、woff/woff2/ttf字体、常见图片格式(png/jpg/jpeg/svg/gif/webp/avif/ico),以及常见音视频格式(mp4/webm/ogv/mov/m4v/mp3/wav/ogg/oga/m4a/aac/flac/opus/weba);workbox.navigateFallback: 'index.html':对 SPA 导航提供回退,同时通过navigateFallbackDenylist排除/presenter、/notes、/overview、/print、/export等内部路由前缀;manifest:生成的 Web 应用清单包含演示文稿标题、start_url(即部署base路径)、display: 'fullscreen',以及#121212的主题色与背景色。
值得注意的是,清单中刻意没有包含任何图标——这并非疏漏,而是设计约束:离线功能本身绝不能依赖远程/CDN 资源,而演示文稿自己的favicon配置可能是一个 URL。不放置图标能让清单始终有效,同时保证这一特性可以纯离线运行。
运行期:Service Worker 注册与状态指示
客户端侧的注册逻辑位于 packages/client/setup/pwa.ts:setupPWA()首先以__SLIDEV_FEATURE_PWA__守卫,然后动态import('virtual:pwa-register')并调用registerSW({ immediate: true, onOfflineReady }),其中onOfflineReady回调会把右下角的"离线就绪"提示在4 秒后自动消失。
指示器的真实界面实现在 packages/client/internals/PwaIndicator.vue:一个固定在右下角、带半透明模糊背景的小标签。它的完整运行体验是:
- 部署并访问
slidev build的产物后,Service Worker 在后台静默下载并缓存全部资源,此时右下角显示Caching for offline…; - 预缓存完成后短暂显示
Ready offline,随后自动淡出; - 此时断开网络并刷新页面,整份演示——包括 HTML、图片与视频——都会直接从缓存中提供服务。
离线能力的边界与注意事项
预缓存并不是"万能离线",以下几点决定了它的适用前提:
- 只有构建产物会被预缓存。在构建时输出(emitted)进产物的文件才会进入缓存;运行时从远程或 CDN 拉取的资源无法离线获取。若希望远程图片也能离线播放,可结合 Bundle Remote Assets 特性,它会在构建阶段把远程资源下载并打进产物。
- 清单不包含图标。如前述,为保证不依赖任何远程/CDN 图标,生成的 Web 应用清单有意不含
icons字段,但仍是合法清单。 - 大媒体文件的容量保障。得益于 100 MB 的单文件上限与覆盖常见图片/视频/音频扩展名的预缓存 glob,体积较大的媒体不会因 Workbox 默认限制而被静默跳过。
- 离线拖拽视频进度可能受限。预缓存的媒体以完整缓存响应(full cached response)方式提供,因此从头播放可以正常工作;而离线状态下对视频进行拖动/跳播(seeking)可能需要额外的 range-request 处理。
推荐组合与小结
一句话总结这套方案的落地路径:在演示文档第一页 headmatter 写入pwa: 'build'(或pwa: true),提前(或按 CLI 提示)安装vite-plugin-pwa,执行slidev build后把产物托管到任意静态服务器——首次访问后即进入"完全离线可用"状态。若演示中包含远程图片,再叠加 Bundle Remote Assets;配合 部署与托管 指南可完成整套自托管流程。
从实现层面看,这一特性是"编译期开关 + 按需可选依赖 + 客户端守卫"三者的精巧配合:pwa关闭时从依赖到代码都被彻底旁路,开启时则由 Workbox 承担预缓存、由vite-plugin-pwa承担 Service Worker 生成,最终换来的是演讲稿开场前一次加载、整场演讲无忧播放的可靠体验。
【免费下载链接】slidevPresentation Slides for Developers项目地址: https://gitcode.com/GitHub_Trending/sl/slidev
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考