pwa
将你的 VuePress 站点变成渐进式网络应用程序 (PWA)[1]。
使用
npm i -D @vuepress/plugin-pwa@nextimport { pwaPlugin } from '@vuepress/plugin-pwa'
export default {
plugins: [
pwaPlugin({
// 选项
}),
],
}此插件使用 workbox-build 生成 Service Worker 文件,并使用 register-service-worker 注册 Service Worker。
一个 PWA 使用 Service Worker[2] (简称 SW) 来缓存并代理网站内容。
注意
如果你启用过该插件,并想要禁用它,你可能需要 @vuepress/plugin-remove-pwa 来移除现有的 Service Worker。
指南
网络 App 清单
为了使你的网站符合 PWA 的要求,一个网络 App 清单[3]文件是必要的,并且你的 PWA 应满足可安装性[4]要求。
你可以通过设置 manifest 选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifest 或 manifest.json。前者优先级更高。
插件会自动为你生成 manifest.webmanifest,并在每个页面中添加清单链接声明,但是 你至少应该通过 manifest.icons 或 PWA 插件中的其他选项设置一个有效的图标。
注意
可安装性[4:1]规范要求 manifest 中至少声明一个有效的图标。
所以如果你不配置 manifest.icons,访问者只能享受到 Service Worker 缓存带来的离线可访问性,而并不能作为 PWA 进行安装。
部分清单字段在未设置时会回退到预设值:
name:siteConfig.title||siteConfig.locales['/'].title||"Site"short_name:siteConfig.title||siteConfig.locales['/'].title||"Site"description:siteConfig.description||siteConfig.locales['/'].description||"A site built with vuepress"lang:siteConfig.locales['/'].lang||siteConfig.langstart_url:context.basescope:context.basedisplay:"standalone"theme_color:themeColor||"#46bd87"background_color:"#ffffff"orientation:"portrait-primary"prefer_related_applications:false
我们建议你为你的站点设置 favicon。
此外,该插件默认不处理清单中的任何内容,而是按原样输出。这意味着,如果你计划部署到子目录,则应自行将 URL 前缀附加到自己的清单 URLs 中。如果你需要的所有内容都在 base 文件夹下,你可以在插件选项中设置 appendBase 为 true 让插件将 base 自动附加到任何地址。
缓存控制
为了更好的控制 Service Worker 可以预缓存的内容,插件提供了相关的缓存控制选项。
默认缓存
默认情况下插件会预缓存所有的 js 和 css 文件,但仅缓存主页和 404 页面的 HTML。插件同时还会缓存字体文件 (woff, woff2, eot, ttf, otf) 和 SVG 图标。
图片缓存
如果你的站点只有少量重要图片,并希望它们在离线模式下显示,你可以通过设置 cacheImage 为 true 来缓存站点图片。
我们通过文件后缀名识别图片,任何以 .png, .jpg, .jpeg, .gif, .bmp, .webp 结尾的文件都会视为图片。
HTML 缓存
当你网站体积不大,并且希望文档完全离线可用时,你可以通过设置 cacheHTML 为 true 来缓存所有 HTML 页面。
为什么默认不缓存非主页和 404 页面
虽然说 VuePress 为所有的页面通过 SSG[5] 生成了 HTML 文件,但是这些文件主要用于 SEO[6],并能够让你在后端不做 SPA[7] 配置的情况下能够直接访问任何链接。
VuePress 本质上是一个 SPA。这意味着你只需要缓存主页并从主页进入即可正常访问所有页面。所以默认不缓存其他 HTML 能够有效减小缓存大小 (可以缩减大约 40% 的体积),加快 SW 更新速度。
但是这样做也有缺点,如果用户直接从非主页进入网站,首个页面的 HTML 文件仍需要从互联网加载。同时离线环境下,用户只能通过主页进入再自行导航到对应页面,直接访问某个链接会出现无法访问的提示。
大小控制
为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。你可以通过 maxSize 和 maxImageSize 来自定义大小限制 (单位为 KB)。
maxSize 具有最高优先级,任何超过此值的文件都会被排除。所以你如果生成了很大的 HTML 或 JS 文件,请考虑调高此值,否则你的 PWA 可能无法在离线模式下正常运行。
maxImageSize 不能大于 maxSize。
更新控制
update 选项控制用户如何接收更新,其默认值为 "available"。
"available": 新的 SW 会在后台静默安装并获取资源,安装结束后弹窗提示用户新内容就绪,用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。"hint": 用户在进入文档后数秒内就可以收到新内容已发布的通知,并可选择立即刷新。如果用户选择刷新,页面会立即重新加载,随后新的 SW 完成安装并接管页面。其负面效果是,用户在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。"disable": 新的 SW 将在后台完全静默安装并在安装后等待。当旧版本 SW 控制的页面全部关闭后,新 SW 将在下次访问时接管并提供新内容给用户。此设置可以避免用户在访问中被弹窗打扰。"force": 检测到新 SW 后页面会被立即强制刷新,以确保用户浏览最新内容。最大的缺点就是当新 SW 发布后,用户在重新进入网站后的几秒内会遇到预期之外的突然刷新。
提示
文档的更新方式由以前的版本控制,因此当前选项仅影响此版本的下一次更新。
更新提示弹窗
当检测到新内容 (检测到新的 Service Worker) 时,更新提示弹窗将会出现;当新内容就绪时,更新就绪弹窗将会出现。
如果你对默认的弹窗不满意,你可以自行编写组件更换。从 @vuepress/plugin-pwa/client 中导入 PwaFoundPopup 或 PwaReadyPopup 并使用其 slot 来自定义弹窗内容,然后将组件路径传递给 foundComponent 或 readyComponent 选项。
<script setup lang="ts">
import { PwaFoundPopup } from '@vuepress/plugin-pwa/client'
</script>
<template>
<PwaFoundPopup v-slot="{ found, refresh }">
<div v-if="found">
已找到新内容
<button type="button" @click="refresh">刷新</button>
</div>
</PwaFoundPopup>
</template><script setup lang="ts">
import { PwaReadyPopup } from '@vuepress/plugin-pwa/client'
</script>
<template>
<PwaReadyPopup v-slot="{ isReady, reload }">
<div v-if="isReady">
新内容已就绪
<button type="button" @click="reload">应用</button>
</div>
</PwaReadyPopup>
</template>其他选项
插件还提供了其他 PWA 相关选项,比如微软磁贴图标与颜色设置,苹果图标 (apple) 等。如果你是一个高级用户,你也可以设置 generateSWConfig 来配置 workbox-build。
选项
string'service-worker.js'Service Worker 文件路径。
booleantrue是否在 Service Worker 首次成功注册时显示 PWA 安装按钮。
AppManifest将被解析为 manifest.webmanifest 的对象,该文件由插件生成并注入到每个页面。
参考:网络 App 清单。
stringfavicon.ico 地址。
string'#46bd87'PWA 的主题色。
ApplePwaOptions | false支持苹果的特殊设置,忽略它们是安全的。
string填入苹果使用的图标地址,推荐 152×152 大小。
stringSafari 图标。
'black-translucent' | 'black' | 'default''default'Safari 状态栏颜色。相关标签尚未标准化,你应该避免声明它。
Partial<GenerateSWOptions>传递给 workbox-build 的选项,具体详情,请见 Workbox 文档。
LocaleConfig<PwaPluginLocaleData>PWA 插件的国际化配置,各语言的数据为 PwaPluginLocaleData 的一部分。
参考:多语言配置。
string安装按钮文字。
stringiOS 安装文字。
string取消按钮文字。
string关闭按钮文字。
string上一张图片文字。
string下一张图片文字。
string安装解释。
string描述标签文字。
string特性标签文字。
string更新内容提示文字。
string更新内容可用文字。
组合式 API
usePwaEvent
类型:
() => PwaEvent返回值:插件的事件发射器
详情:返回此插件的事件派发器。你可以添加监听器函数到 register-service-worker 提供的事件。
示例:
import { usePwaEvent } from '@vuepress/plugin-pwa/client' export default { setup(): void { const event = usePwaEvent() event.on('ready', (registration) => { console.log('Service worker is active.') }) }, }
工具函数
forceUpdate
类型:
() => void详情:当发现新内容时强制刷新页面。
示例:
import { forceUpdate } from '@vuepress/plugin-pwa/client' import { onMounted } from 'vue' export default { setup(): void { onMounted(() => { forceUpdate() }) }, }
registerSW
类型:
(serviceWorkerPath: string, hooks?: Hooks, showStatus?: boolean) => Promise<void>参数:
参数 类型 描述 serviceWorkerPath stringService worker 的路径 hooks objectService worker 的钩子 showStatus boolean在控制台输出状态日志 interface Hooks { registrationOptions?: RegistrationOptions ready?: (registration: ServiceWorkerRegistration) => void registered?: (registration: ServiceWorkerRegistration) => void cached?: (registration: ServiceWorkerRegistration) => void updated?: (registration: ServiceWorkerRegistration) => void updatefound?: (registration: ServiceWorkerRegistration) => void offline?: () => void error?: (error: Error) => void }详情:手动注册 Service Worker。
示例:
import { registerSW } from '@vuepress/plugin-pwa/client' import { onMounted } from 'vue' export default { setup(): void { onMounted(() => { registerSW('/service-worker.js', { ready(registration) { console.log('Service worker is active.') }, }) }) }, }
skipWaiting
类型:
(registration: ServiceWorkerRegistration) => void参数:
参数 类型 描述 registration ServiceWorkerRegistration想要激活的 Service Worker 的注册 详情:激活等待中的 Service Worker。
示例:
import { skipWaiting, usePwaEvent } from '@vuepress/plugin-pwa/client' export default { setup(): void { const event = usePwaEvent() event.on('updated', (registration) => { console.log('The waiting service worker is available.') // activate the waiting service worker skipWaiting(registration) }) }, }
unregisterSW
类型:
() => Promise<boolean>返回值:
true表示注销成功,false表示注销失败详情:手动注销 Service Worker。
示例:
import { unregisterSW } from '@vuepress/plugin-pwa/client' import { onMounted } from 'vue' export default { setup(): void { onMounted(() => { unregisterSW() }) }, }
样式
你可以通过 CSS 变量来自定义样式:
:root {
--pwa-z-index: 10;
--pwa-c-bg: var(--vp-c-bg-elv);
--pwa-c-text: var(--vp-c-text);
--pwa-c-shadow: var(--vp-c-shadow);
--pwa-c-accent-bg: var(--vp-c-accent-bg);
--pwa-c-accent-hover: var(--vp-c-accent-hover);
--pwa-c-accent-text: var(--vp-c-accent-text);
--pwa-c-control: var(--vp-c-control);
--pwa-c-control-hover: var(--vp-c-control-hover);
--pwa-c-text-mute: var(--vp-c-text-mute);
}相关阅读
更多内容,请详见:
PWA 介绍
PWA 全称 Progressive Web app,即渐进式网络应用程序,标准由 W3C 规定。
它允许网站通过支持该特性的浏览器将网站作为 App 安装在对应平台上。
访问 https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps 查看详情。 ↩︎
Service Worker 简要介绍
Service Worker 会在注册过程中获取注册在其中的所有文件并缓存它们。
注册成功后,Service Worker 激活,并开始代理并控制你的全部请求。
每当你想要通过浏览器发起访问请求后,Service Worker 将会查看其是否存在与自身缓存列表中,若存在则直接返回缓存好的结果,否则调用自身的 fetch 方法进行获取。你可以通过自定义 fetch 方法,来完全控制网页内资源获取请求的结果,比如在离线时提供一个 fallback 的网页。
每次用户重新打开网站时,Service Worker 会向自身注册时的地址发出校验命令,如果检测到新版本的 Service Worker,则会更新自身,并开始缓存注册在新 Service Worker 中的资源列表。成功获取内容更新后,Service Worker 将会触发
update事件。可以通过此事件提示用户,比如将在右下角显示一个弹出窗口,提示用户新内容可用并允许用户触发更新。
清单文件
清单文件使用 JSON 格式,负责声明 PWA 各项信息,如名称、描述、图标、快捷动作等。
为了使你的站点能够被注册为 PWA,你需要满足 manifest 基本的规范,才能使浏览器认为该网站为一个可安装的 PWA 并允许用户安装它。
↩︎提示
Manifest 的标准与规范,请详见 MDN 网络 App 清单 和 W3C Manifest
可安装性
想要让网站可以注册为 PWA,网站需要自行成功注册有效的 Service Worker,同时拥有合法的 manifest 清单文件并在网站中声明它。
清单文件应至少包含
name(或short_name)、icons和start_url。SSG: Static Site Generating,静态站点生成。 ↩︎
SEO: Search Engine Optimization,搜索引擎增强,
SPA: Single Page Application, 单页应用
大多只有主页,并使用 history mode 处理路由,而不是真的在页面之间导航。 ↩︎
