跳转到内容

为 Web 框架添加 PWA 支持

发布于

本指南为 Next.js、Nuxt、SvelteKit 或 Angular 项目添加 Web App Manifest 与 Service Worker,各框架保留自己的构建集成,浏览器端产物则使用相同的 Manifest 和 Service Worker API。

MDN 把 Service Worker API 标为广泛可用。注册要求安全上下文,浏览器把 localhost 视为本地开发所需的安全上下文,因此示例会在无法注册或注册失败时让应用继续通过网络使用。

请先选择负责应用构建的框架小节,再使用最后一个小节,在浏览器无法注册时保留联网路径。

在 Next.js App Router 中添加 manifest 路由

链接到“在 Next.js App Router 中添加 manifest 路由”一节

Next.js 通过 app/manifest.ts 提供带类型的 manifest 路由。其 PWA 指南在 lib/service-worker.js 创建自定义 Service Worker,并从客户端组件使用 new URL("../lib/service-worker.js", import.meta.url) 注册。

用应用实际使用的字段创建 manifest,并把引用的图标文件放入 public/。

app/manifest.ts
import type { MetadataRoute } from "next";
export default function manifest(): MetadataRoute.Manifest {
return {
name: "Field Notes",
short_name: "Notes",
start_url: "/",
display: "standalone",
background_color: "#ffffff",
theme_color: "#12372a",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
};
}

添加 lib/service-worker.js,再使用下方 Next.js 注册示例进行能力检测和失败处理。

Nuxt 模块目录为 Nuxt 3 提供 @vite-pwa/nuxt,添加模块后,把 manifest 数据放在 pwa 下。

运行 npx nuxi@latest module add @vite-pwa/nuxt 安装,然后在 nuxt.config.ts 中保存配置。

nuxt.config.ts
export default defineNuxtConfig({
modules: ["@vite-pwa/nuxt"],
pwa: {
registerType: "autoUpdate",
manifest: {
name: "Field Notes",
short_name: "Notes",
start_url: "/",
display: "standalone",
theme_color: "#12372a",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
},
},
});

该模块依据此配置生成 Service Worker 并注入 Web App Manifest。

Vite PWA 的 SvelteKit 集成提供 SvelteKitPWA。其文档在 vite.config.ts 中配置插件,并通过 virtual:pwa-info 暴露 manifest 标记。

把 @vite-pwa/sveltekit 安装为开发依赖,再把插件放在 sveltekit() 之后。

vite.config.ts
import { sveltekit } from "@sveltejs/kit/vite";
import { SvelteKitPWA } from "@vite-pwa/sveltekit";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
sveltekit(),
SvelteKitPWA({
manifest: {
name: "Field Notes",
short_name: "Notes",
start_url: "/",
display: "standalone",
theme_color: "#12372a",
icons: [
{ src: "/icon-192.png", sizes: "192x192", type: "image/png" },
{ src: "/icon-512.png", sizes: "512x512", type: "image/png" },
],
},
}),
],
});

按照插件文档,把生成的 manifest 链接加入根布局。

src/routes/+layout.svelte
<script>
import { pwaInfo } from "virtual:pwa-info";
$: webManifestLink = pwaInfo ? pwaInfo.webManifest.linkTag : "";
</script>
<svelte:head>
{@html webManifestLink}
</svelte:head>

虚拟模块没有 manifest 信息时,空字符串回退让布局保持有效。

Angular 的 @angular/pwa schematic 会添加 Service Worker 包、构建支持、注册代码、manifest 链接、图标以及 ngsw-config.json。

在项目目录应用 schematic,再创建生产构建。

Terminal window
ng add @angular/pwa
ng build

检查生成的 manifest.webmanifest、图标与 ngsw-config.json,不要把起始值直接当成应用元数据;Angular 指南在本地演示 Service Worker 时使用生产配置。

注册 Next.js worker 并保留联网路径

链接到“注册 Next.js worker 并保留联网路径”一节

框架插件自动生成构建产物,但浏览器仍通过 navigator.serviceWorker 暴露 Service Worker 能力。下面的 Next.js 客户端组件采用框架指南中的 worker URL。

不支持或注册失败时,普通网络导航保持可用。

把组件保存为 app/service-worker-registration.tsx,与上文指定的 lib/service-worker.js 配合使用,再从布局或页面渲染 <ServiceWorkerRegistration />。

app/service-worker-registration.tsx
"use client";
import { useEffect, useState } from "react";
export default function ServiceWorkerRegistration() {
const [status, setStatus] = useState("正在检查 Service Worker 支持…");
useEffect(() => {
if (!("serviceWorker" in navigator)) {
setStatus("Service Worker 注册不可用;应用仍可联网使用。");
return;
}
let cancelled = false;
navigator.serviceWorker
.register(
new URL("../lib/service-worker.js", import.meta.url),
{ scope: "/", updateViaCache: "none" },
)
.then(() => {
if (!cancelled) setStatus("Service Worker 注册已就绪。");
})
.catch((error) => {
if (!cancelled) {
setStatus("Service Worker 注册失败;应用仍可联网使用。");
}
console.error(error);
});
return () => {
cancelled = true;
};
}, []);
return <p role="status">{status}</p>;
}

Next.js 自定义 worker 使用上述直接注册。Nuxt 与 SvelteKit 插件及 Angular schematic 会提供自己的注册路径;在它们初始化的界面中保留相同的支持与错误状态。