网站开发设置PWA(Progressive Web App,渐进式 Web 应用)

生活日记 工作心得
131
发表时间: 编辑:顾永胜来源:0513.city标签:网站开发

1.下载workbox-v7.4.1本地JS版本:workbox-v7.4.1

将文件解压到public/static/js文件里面。

2.配置manifest.json,最简能用配置:

{
  "name""citya",
  "short_name""citya",
  "start_url""/",
  "display""standalone",
  "background_color""#ffffff",
  "theme_color""#ffffff",
  "icons": [
    {
      "src""/static/img/icon-192x192.png",
      "sizes""192x192",
      "type""image/png"
    },
    {
      "src""/static/img/icon-512x512.png",
      "sizes""512x512",
      "type""image/png"
    }
  ]
}

manifest.json文件放在public文件夹里。

html页面引用:

<link rel="manifest" href="/manifest.json">

3.写一个安装app.js,内容如下:

(function () {
  'use strict';
  if (!('serviceWorker' in navigator)) return;
  window.addEventListener('load'function () {
    navigator.serviceWorker.register('/sw.js')
      .catch(function (err) {
        if (window.Sentrywindow.Sentry.captureException(err);
      });
  });
})();

app.js文件放到public/static/js目录中。

4.创建sw.js文件,内容如下:

importScripts('/static/js/workbox-v7.4.1/workbox-sw.js');
const RUNTIME_VERSION = 'v3.0.5';
workbox.setConfig({
  modulePathPrefix: '/static/js/workbox-v7.4.1/'
});
/**
 * 管理内容不能缓存
 */
self.addEventListener('fetch', (event=> {
  if (event.request.destination === 'manifest'return;
  const url = new URL(event.request.url);
  if (url.pathname.startsWith('/admin')) {
    const headers = new Headers(event.request.headers);
    headers.set('Cache-Control''no-cache, no-store, must-revalidate');
    headers.set('Pragma''no-cache');
    const newRequest = new Request(event.request, { headers });
    event.respondWith(fetch(newRequest));
    return;
  }
});
/**
 * 安装后立即激活
 */
self.addEventListener('install', () => {
  self.skipWaiting();
});
/**
 * 预缓存核心静态资源
 */
 workbox.precaching.precacheAndRoute([
  { url: '/offline.html'revision: '1' }, 
  { url: '/static/css/style.css'revision: '1' },
  { url: '/static/js/app.js'revision: '1' }
]);
/**
 * /admin 路径:只走网络(Workbox 层面保险)
 */
workbox.routing.registerRoute(
  ({ url }) => url.pathname.startsWith('/admin'),
  new workbox.strategies.NetworkOnly()
);
/**
 * 带参数的请求:不缓存
 */
workbox.routing.registerRoute(
  ({ url }) => url.search.length > 0,
  new workbox.strategies.NetworkOnly()
);
/**
 * HTML 页面
 */
workbox.routing.registerRoute(
  ({ requesturl }) => {
    return request.mode === 'navigate' && !url.pathname.startsWith('/admin');
  },
  new workbox.strategies.NetworkFirst({
    cacheName: `html-cache-${RUNTIME_VERSION}`,
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxEntries: 500,
      }),
      {
        handlerDidError: async () => {
          const offlineResp = await workbox.precaching.matchPrecache('/offline.html');
          if (offlineRespreturn offlineResp;
          return new Response('离线,暂无缓存', {
            status: 503,
            headers: { 'Content‑Type': 'text/html;charset=utf‑8' }
          });
        }
      }
    ]
  })
);
/**
 * 图片:先缓存后台更新
 */
workbox.routing.registerRoute(
  ({ request }) => request.destination === 'image',
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: `image-cache-${RUNTIME_VERSION}`,
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxEntries: 500,
      })
    ]
  })
);
/**
 * JS / CSS / 字体:永久缓存
 */
workbox.routing.registerRoute(
  ({ request }) =>
    request.destination === 'script' ||
    request.destination === 'style' ||
    request.destination === 'font',
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: `static-cache-${RUNTIME_VERSION}`,
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxEntries: 500,
      }),
      new workbox.cacheableResponse.CacheableResponsePlugin({
        statuses: [0200]
      })
    ]
  })
);
/**
 * POST/PUT/DELETE/PATCH:只走网络
 */
workbox.routing.registerRoute(
  ({ request }) => ['POST''PUT''DELETE''PATCH'].includes(request.method),
  new workbox.strategies.NetworkOnly()
);
/**
 * API 只能通过网络
 */
workbox.routing.registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new workbox.strategies.NetworkOnly()
);
/**
 * 激活时清理旧缓存
 */
self.addEventListener('activate', (event=> {
  event.waitUntil(
    caches.keys().then((cacheNames=> {
      return Promise.all(
        cacheNames.map((name=> {
          // 清理 html-cache 里已缓存的 admin 页面
          if (name === `html-cache-${RUNTIME_VERSION}`) {
            return caches.open(name).then((cache=> {
              return cache.keys().then((requests=> {
                return Promise.all(
                  requests
                    .filter((req=> req.url.includes('/admin'))
                    .map((req=> cache.delete(req))
                );
              });
            });
          }
          // 保留 workbox 自身缓存
          if (name.startsWith('workbox-')) return Promise.resolve();
          // 保留当前版本的缓存
          if (name.endsWith(`-${RUNTIME_VERSION}`)) return Promise.resolve();
          return caches.delete(name);
        })
      );
    }).then(() => self.clients.claim())
  );
});

sw.js文件,放在public目录中。offline.html是用于没有缓存也没有网络的时候显示的提示页面:

5.Html页面引用app.js文件:

<script src="/static/js/app.js" defer>script>

经过上面的配置,网站就有了基本离线功能,只要打开过的网页,在没有网络的情况下也能够打开预览。