给nextjs项目插上pwa的翅膀

2,111 阅读2分钟

本篇主要介绍pwa的相关内容,nextjs相关的请参考nextjs项目以及vercel发布

vercel发布tips

当项目发布到vercel以后

  • 将修改的内容push到分支后,会自动触发development环境的deploy
  • 将修改的内容merge到master并push后,会自动触发production环境的deploy

pwa知识点

三个必备条件

  • https站点 必须 service worker只能在https上使用,localhost除外
  • 一个或多个service worker 必须 控制应用的网络请求,以及是否使用缓存响应网络请求
  • manifest.json文件 非必须 描述应用的名字,启动url,icons等其他信息,需要安装应用的时候才需要此文件

service worker生命周期

上一个经典的图

对应上图

  • installing阶段 注册sw,在此阶段可以设置cache缓存
    • install事件
      • event.waitUntil() 延长installing阶段,用来自定义操作,比如设置缓存
      • self.skipWaiting() 结束延长,进入下一阶段
  • installed阶段 sw设置完成
  • activating 主要用来清理旧缓存或其他sw的资源
    • activate事件
      • event.waitUntil() 延长activating阶段,清理旧缓存
      • self.clients.claim() 结束延长,进入下一阶段
  • activated sw可以处理functional events
  • redundant sw被另一个sw替代

service worker可使用的event

四步让next.js具备pwa

  • /public目录中新增manifest.json
  • /public目录中新增service-worker.js service-worker.js必须在访问域名的根域名下,所以必须放在public目录下
  • /pages/_document.js文件中的Head中插入<link rel="manifest" href="/manifest.json" />
  • /pages/_app.js文件中注册service-worker.js
// _app.js
import React, {useEffect} from 'react';
import '../styles/globals.css'
import '../styles/antd.less';
import sw from '../utils/sw.js';

function MyApp({ Component, pageProps }) {
  useEffect(()=>{
    if ('serviceWorker' in navigator) {

      // register service worker
      navigator.serviceWorker.register('../service-worker.js');
    }
  }, [])
  return <Component {...pageProps} />
}

export default MyApp
// manifest.json
{
  "name"              : "会员",
  "short_name"        : "会员",
  "description"       : "会员",
  "start_url"         : "/",
  "display"           : "standalone",
  "orientation"       : "any",
  "background_color"  : "#ACE",
  "theme_color"       : "#ACE",
  "icons": [
    {
      "src"           : "/logo.jpeg",
      "sizes"         : "72x72",
      "type"          : "image/png"
    },
    {
      "src"           : "/logo.jpeg",
      "sizes"         : "152x152",
      "type"          : "image/png"
    },
    {
      "src"           : "/logo.jpeg",
      "sizes"         : "192x192",
      "type"          : "image/png"
    },
    {
      "src"           : "/logo.jpeg",
      "sizes"         : "256x256",
      "type"          : "image/png"
    },
    {
      "src"           : "/logo.jpeg",
      "sizes"         : "512x512",
      "type"          : "image/png"
    }
  ]
}
// service-worker.js

const
  version = '1.0.2',
  CACHE = version + '::ZiyiMember',
  installFilesEssential = [
    '/',
    '/manifest.json',
    '/favicon.ico',
    '/logo.jpeg',
  ];

// install static assets
function installStaticFiles() {
  return caches.open(CACHE)
    .then(cache => {
      return cache.addAll(installFilesEssential);
    });
}

function clearOldCaches() {
  return caches.keys()
    .then(keylist => {
      return Promise.all(
        keylist
          .filter(key => key !== CACHE)
          .map(key => caches.delete(key))
      );
    });
}


self.addEventListener('install', event => {
  event.waitUntil(
    installStaticFiles()
      .then(() => self.skipWaiting())
  );
});

self.addEventListener('activate', event => {
  event.waitUntil(
    clearOldCaches()
      .then(() => self.clients.claim())
  );
});

self.addEventListener('fetch', event => {
  if (event.request.method !== 'GET') return;
  let url = event.request.url;
  event.respondWith(
    caches.open(CACHE)
      .then(cache => {
        return cache.match(event.request)
          .then(response => {
            if (response) {
              return response;
            }
            return fetch(event.request)
              .then(newreq => {
                console.log('network fetch: ' + url);
                if (newreq.ok) cache.put(event.request, newreq.clone());
                return newreq;

              })
              .catch(()=>null);
          });

      })
  );
});

chrome下查看service-worker和cache

参考文章