关于离线应用在nextjs中的开发实践(pwa)

2,017 阅读3分钟

# 渐进式 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中实现

  1. 下载npm包
# Install package
npm i next-pwa
  1. 在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
});
  1. 在/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"
    }
  ]
}

  1. 将以下内容添加到 _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);
    })
  );
});

最后运行出现就可以安装到桌面,离线情况下就可以访问了!

image.png

参考资料

developer.mozilla.org/zh-CN/docs/… github.com/shadowwalke…