# 渐进式 Web 应用(PWA)
渐进式 Web 应用(Progressive Web App,PWA)是一个使用 web 平台技术构建的应用程序,但它提供的用户体验就像一个特定平台的应用程序。
它像网站一样,PWA 可以通过一个代码库在多个平台和设备上运行。它也像一个特定平台的应用程序一样,可以安装在设备上,可以离线和在后台运行,并且可以与设备和其他已安装的应用程序集成。
渐进式 web 应用结合了传统网站和平台特定应用的最佳特性。
PWA 具有网站的优势,包括:
- PWA 是使用标准的 web 平台技术开发的,所以它们可以从单一代码库在多个操作系统和设备类上运行。
- PWA 可以直接通过 web 访问。
PWA 也具有平台特定应用程序的许多优势,包括:
-
PWA 可以安装在设备上。这意味着:
- PWA 可以从平台的应用商店安装,也可以直接从 web 安装。
- PWA 可以像平台特定的应用程序一样安装,并可以自定义安装过程。
- 一旦安装,PWA 会在设备上得到一个应用图标,与平台特定应用程序一样。
- 一旦安装,PWA 可以作为独立的应用程序启动,而不是作为浏览器中的网站。
-
PWA 可以在后台与离线操作:一个典型的网站只在被浏览器加载后才处于活动状态。PWA 可以:
-
PWA 可以使用整个屏幕,而不是在浏览器 UI 中运行。
-
PWA 可以与设备集成,注册为共享目标和来源,并访问设备特性。
-
PWA 可以在应用商店以及通过 web 公开发布。
实现PWA的流程
首先我们需要一个 manifest.json 文件,它的作用是提供网页应用的描述和信息。例如我们可以在里面设置PWA应用在手机上的名称、图标、网页链接、主题颜色等等。浏览器引擎就可以识别你的网页是可以运行PWA,然后根据在 manifest.json 里的设置去处理网页应用。 再来就是 Service Worker 的部分,上面的 manifest.json 只是让浏览器识别你网站兼容PWA;Service Worker 才是PWA的核心,它是完全独立于浏览器的后台运行脚本。由于有独立的线程,所以可以提供后台同步、离线使用和推送通知等功能。
在nextjs中实现
- 下载npm包
# Install package
npm i next-pwa
- 在next.config.js中新增
// Configuration object tells the next-pwa plugin
const withPWA = require("next-pwa")({
dest: "public", // Destination directory for the PWA files
// disable: process.env.NODE_ENV === "development", // Disable PWA in development mode
// register: true, // Register the PWA service worker
// skipWaiting: true, // Skip waiting for service worker activation
});
- 在/public下新增 manifest.json,对应新建icons文件夹放入以下路径中的图片
{
"name": "next-pwa",
"short_name": "next-pwa",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#FFFFFF",
"background_color": "#FFFFFF",
"start_url": "/",
"icons": [
{
"src": "/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
- 将以下内容添加到
_document.jsx或_app.tsx中<Head>
<meta name="application-name" content="PWA App" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="PWA App" />
<meta name="description" content="Best PWA App in the world" />
<meta name="format-detection" content="telephone=no" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="msapplication-config" content="/icons/browserconfig.xml" />
<meta name="msapplication-TileColor" content="#2B5797" />
<meta name="msapplication-tap-highlight" content="no" />
<meta name="theme-color" content="#000000" />
<link rel="apple-touch-icon" href="/icons/touch-icon-iphone.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/icons/touch-icon-ipad.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/icons/touch-icon-iphone-retina.png" />
<link rel="apple-touch-icon" sizes="167x167" href="/icons/touch-icon-ipad-retina.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#5bbad5" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:url" content="https://yourdomain.com" />
<meta name="twitter:title" content="PWA App" />
<meta name="twitter:description" content="Best PWA App in the world" />
<meta name="twitter:image" content="https://yourdomain.com/icons/android-chrome-192x192.png" />
<meta name="twitter:creator" content="@DavidWShadow" />
<meta property="og:type" content="website" />
<meta property="og:title" content="PWA App" />
<meta property="og:description" content="Best PWA App in the world" />
<meta property="og:site_name" content="PWA App" />
<meta property="og:url" content="https://yourdomain.com" />
<meta property="og:image" content="https://yourdomain.com/icons/apple-touch-icon.png" />
<!-- apple splash screen images -->
<!--
<link rel='apple-touch-startup-image' href='/images/apple_splash_2048.png' sizes='2048x2732' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1668.png' sizes='1668x2224' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1536.png' sizes='1536x2048' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1125.png' sizes='1125x2436' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1242.png' sizes='1242x2208' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_750.png' sizes='750x1334' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_640.png' sizes='640x1136' />
-->
5.通过sevice-worker缓存fetch,离线缓存fetch,在线继续请求接口
useEffect(() => {//判断是否在线
if (
typeof window !== "undefined" &&
"ononline" in window &&
"onoffline" in window
) {
setIsOnline(window.navigator.onLine);
if (!window.ononline) {
window.addEventListener("online", () => {
setIsOnline(true);
});
}
if (!window.onoffline) {
window.addEventListener("offline", () => {
setIsOnline(false);
});
}
}
}, []);
useEffect(() => {//注册sercice-worker
if (
typeof window !== "undefined" &&
"serviceWorker" in navigator &&
window.workbox !== undefined &&
isOnline
) {
window.addEventListener("load", function () {
navigator.serviceWorker.register("/service-worker.js");
});
}
}, [isOnline, router.route]);
// service-worker.ts
self.addEventListener("fetch", function (event) {
event.respondWith(
caches.match(event.request).then(function (response) {
// 如果缓存中有对应资源,则直接返回缓存
if (response) {
return response;
}
// 否则,通过网络请求资源
return fetch(event.request);
})
);
});
最后运行出现就可以安装到桌面,离线情况下就可以访问了!