PWA:渐进式网页应用程序;关键技术:Service Worker、Manifest 、Push Notification;直观上的体现:无网络的时间,可以立即加载出数据和页面 这次主要实践 Service Worker Service Worker Service Worker(SW)是一个离线缓存文件,SW 作用于浏览器与服务器之间,可以理解为一个代理服务器 具体的 Service Worke…

PWA:渐进式网页应用程序;关键技术:Service Worker、Manifest 、Push Notification;直观上的体现:无网络的时间,可以立即加载出数据和页面

这次主要实践 Service Worker

Service Worker

Service Worker(SW)是一个离线缓存文件,SW 作用于浏览器与服务器之间,可以理解为一个代理服务器

具体的 Service Worker 的内部机制和生命周期可以看这篇Service Worker 全面进阶

实践

  1. 新建 4 个文件:1.png、index.html、main.css、sw.js,均在同一级
  2. 前提:chrome 开启隐身模式,有一台 Nginx/Apache 服务器
  3. sw.js 文件的更新是一个比较坑。因为它的替换是当 older SW 不工作的时候 new SW 才能发生作用,不工作包含浏览器关闭,但是浏览器关闭是一个未知数
  4. sw 的更新有两种,一是手动关闭浏览器,二是更改 storage 的 key
  • index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>simple PWA demo</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
    <link rel="stylesheet" href="./main.css" />
  </head>
  <body>
    <h1>simple PWA demo56</h1>
    <img src="./1.png" alt="" />
  </body>
  <script>
    // 检测浏览器是否支持SW
    // SW 就是一个网络代理,用来捕获你网页的所有 fetch 请求
    // SW 在你网页加载完成同样也能捕获已经发出的请求
    // 如果想要测试注册 SW 可以使用隐身模式调试, 那当注册成功时,打开chrome://inspect/#service-workers查看注册的 SW
    // chrome://serviceworker-internals,用来查看当前浏览器中,所有注册好的 SW
    window.addEventListener("DOMContentLoaded", function() {
      if (navigator.serviceWorker != null) {
        navigator.serviceWorker
          .register("sw.js")
          .then((registartion) => {
            console.log("支持sw:", registartion.scope);
          })
          .catch((error) => {
            console.log(error);
          });
      }
    });
  </script>
</html>
  • main.css
h1 {
  font-weight: bold;
}
  • sw.js
  1. sw 会检测制定的文件的缓存问题,如果缓存了,读取缓存,如果没有缓存就会发送请求获取;如果一次文件下载失败,sw 就会启动失败
  2. sw 有 3 个声明周期:一是监听安装阶段(install),用于缓存文件;二是缓存捕获(fetch):用于捕获请求;三是激活阶段:用于数据更新;每次数据更新都会重新出发安装监听阶段(install)
var workbox = (function() {
  "use strict";
  try {
    self.workbox.v["workbox:sw:3.1.0"] = 1;
  } catch (t) {}
  const t = "https://storage.googleapis.com/workbox-cdn/releases/3.1.0",
    e = {
      backgroundSync: "background-sync",
      core: "core",
      expiration: "cache-expiration",
      googleAnalytics: "google-analytics",
      strategies: "strategies",
      precaching: "precaching",
      routing: "routing",
      cacheableResponse: "cacheable-response",
      broadcastUpdate: "broadcast-cache-update",
      rangeRequests: "range-requests"
    };
  return new (class {
    constructor() {
      return (
        (this.v = {}),
        (this.t = {
          debug: "localhost" === self.location.hostname,
          modulePathPrefix: null,
          modulePathCb: null
        }),
        (this.e = this.t.debug ? "dev" : "prod"),
        (this.s = !1),
        new Proxy(this, {
          get(t, s) {
            if (t[s]) return t[s];
            const o = e[s];
            return (
              o &&
                t.loadModule(`workbox - $ {
                      o
                  }`),
              t[s]
            );
          }
        })
      );
    }
    setConfig(t = {}) {
      if (this.s) throw new Error("Config must be set before accessing workbox.* modules");
      Object.assign(this.t, t), (this.e = this.t.debug ? "dev" : "prod");
    }
    skipWaiting() {
      self.addEventListener("install", () => self.skipWaiting());
    }
    clientsClaim() {
      self.addEventListener("activate", () => self.clients.claim());
    }
    loadModule(t) {
      const e = this.o(t);
      try {
        importScripts(e), (this.s = !0);
      } catch (s) {
        throw (console.error(`Unable to import module '${t}'
              from '${e}'.`),
        s);
      }
    }
    o(e) {
      if (this.t.modulePathCb) return this.t.modulePathCb(e, this.t.debug);
      let s = [t];
      const o = `$ {
              e
          }.$ {
              this.e
          }.js`,
        r = this.t.modulePathPrefix;
      return r && "" === (s = r.split("/"))[s.length - 1] && s.splice(s.length - 1, 1), s.push(o), s.join("/");
    }
  })();
})();

// 定义的一些缓存key名与缓存文件
var cacheStorageKey = "minimal-pwa-1";
var cacheList = ["/pwa", "index.html", "main.css", "1.png"];
// 监听安装sw代码:缓存文件
// sw会检测制定的文件的缓存问题,如果缓存了,读取缓存,如果没有缓存就会发送请求获取
// 如果一次文件下载失败,sw就会启动失败
self.addEventListener("install", (e) => {
  self.skipWaiting();
  e.waitUntil(
    caches
      .open(cacheStorageKey)
      .then((cache) => cache.addAll(cacheList))
      .then(() => self.skipWaiting())
  );
});
//缓存捕获:用于cache进行缓存
self.addEventListener("fetch", function(e) {
  // 包含响应主页面请求的代码
  e.respondWith(
    caches.match(e.request).then(function(response) {
      if (response != null) {
        return response;
      }
      return fetch(e.request.url);
    })
  );
});
// 激活阶段:用于数据更新
self.addEventListener("activate", function(e) {
  e.waitUntil(
    //获取所有cache名称
    caches
      .keys()
      .then((cacheNames) => {
        return Promise.all(
          // 获取所有不同于当前版本名称cache下的内容
          cacheNames
            .filter((cacheNames) => {
              return cacheNames !== cacheStorageKey;
            })
            .map((cacheNames) => {
              return caches.delete(cacheNames);
            })
        );
      })
      .then(() => {
        return self.clients.claim();
      })
  );
});

暂无评论