了解Service Worker和实战

分类:前端来源:站内 最近更新:2020-10-07 11:22:26浏览:1400留言:0

前言

    我们在用VUE-CLI创建项目的时候,有个"Progressive Web App (PWA) Support"选项,勾选上之后就会在src目录里生成registerServiceWorker.js的文件,代码如下:

/* eslint-disable no-console */
import { register } from "register-service-worker";
if (process.env.NODE_ENV === "production") {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready() {
      console.log(
        "App is being served from cache by a service worker.\n" +
          "For more details, visit https://goo.gl/AFskqB"
      );
    },
    registered() {
      console.log("Service worker has been registered.");
    },
    cached() {
      console.log("Content has been cached for offline use.");
    },
    updatefound() {
      console.log("New content is downloading.");
    },
    updated() {
      console.log("New content is available; please refresh.");
    },
    offline() {
      console.log(
        "No internet connection found. App is running in offline mode."
      );
    },
    error(error) {
      console.error("Error during service worker registration:", error);
    }
  });
}

    2015年互联网+时代的到来,让Native App的安卓开发和IOS开发火了一把,当热潮消退之后,Native APP的缺点也慢慢的暴露出来:

  • 开发成本高,因为这个行业刚刚兴起,人才很紧缺,不仅薪资高,而且真正的经验也有限。

  • 一个APP要上传到不同的应用商店,大大小小的不下20个。

  • 版本上线需要各个应用商店的审核,尤其遇到大版本迭代和搞活动。时间上的控制会让公司疯掉

    所以,很多公司又把目光转移到web端的H5上,但是web的缺点依旧是有的,至少体验上没有Native APP好,没有网路就不能打开,不具备离线的能力。所以慢慢的,PWA渐进式WEB应用诞生了,PWA的离线功能就是用Service Worker来实现的。


什么是Service Worker

    Service worker是一个注册在指定源和路径下的事件驱动woker。它采用JavaScript控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。(官方描述);

    如果熟悉Web Worker,了解Service Worker就简单了。JS是单线程的,但是单线程已经满足不了快节奏的趋势,如何解决这个弊端,Web Worker 和 Service Worker就创造出来了,他们是在主线程之外独立运行的线程。通过postMessage和onMessage和主线程相互通信。Service Worker具备以下特点:

  • 独立的线程,在web worker的基础上增加了离线缓存的能力

  • 无法操作DOM

  • 本质上充当Web应用程序(服务器)与浏览器之间的代理服务器(可以拦截全站的请求,并作出相应的动作->由开发者指定的动作)

  • 只能使用HTTPS以及localhost(测试环境修改host文件绑定)

  • 由事件驱动的,具有生命周期

  • 可以访问cache和indexDB

  • 一旦被 install,就永远存在,除非被 uninstall或者dev模式手动删除

  • 支持推送

  • 并且可以让开发者自己控制管理缓存的内容以及版本


兼容性

    Service worker大部分应用在H5手机web端,所以市面上的大部分手机浏览器应用都支持了。如果不支持,JS里加上判断,对整个程序完全没有任何影响。

1601357734509213172.jpg


生命周期

1601358199943483827.jpg

  1. 注册事件由主线程发起,注册一个serviceWorker,如文章最顶部VUE脚手架自带的文件,用户打开页面时,开始注册Service Workder.

  2. installing 注册中会触发 install 事件,serviceWorker都是事件触发的方式进行的逻辑调用,如果事件里有 event.waitUntil() 则会等待传入的 Promise 完成才会成功

  3. 注册完成,如果之前有就的Service Worker脚本控制,直接通过self.waitUntil()跳过等待

  4. 安装完成后,开始进入激活activating,

  5. 等待event.waitUntil()完成后,出发activated

  6. 当用户再次刷新页面或者再次进入后,先读取serviceWorker存的版本资源,如果有变化,会重新触发生命周期。


实战

1、先创建一个主程序用于加载独立的线程js文件(当然这边的方式有很多种,可以是blob流,也可以用script的方式写)。

核心代码如下:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('service-worker.js');
}

vue脚手架会自动在src里建立registerServiceWorker.js,核心代码文章顶部。下面就以vue-cli脚手架为实战练习

2、根据代码${process.env.BASE_URL}service-worker.js,需要在public目录下新建一个service-worker.js文件。

编辑代码如下:

var CACHE_VERSION = "app-v1"; // 缓存文件的版本
var CACHE_FILES = [
  // 需要缓存的页面文件
  "/"
];
self.addEventListener("install", function(event) {
  // 监听worker的install事件
  event.waitUntil(
    // 延迟install事件直到缓存初始化完成

    caches.open(CACHE_VERSION).then(function(cache) {
      console.log("Opened cache");
      return cache.addAll(CACHE_FILES);
    })
  );
});

self.addEventListener("activate", function(event) {
  // 监听worker的activate事件
  event.waitUntil(
    // 延迟activate事件直到
    caches.keys().then(function(keys) {
      return Promise.all(
        keys.map(function(key, i) {
          // 清除旧版本缓存
          if (key !== CACHE_VERSION) {
            return caches.delete(keys[i]);
          }
        })
      );
    })
  );
});

self.addEventListener("fetch", function(event) {
  // 截取页面的资源请求
  event.respondWith(
    // 返回页面的资源请求
    caches.match(event.request).then(function(res) {
      // 判断缓存是否命中
      if (res) {
        // 返回缓存中的资源
        return res;
      }
      requestBackend(event); // 执行请求备份操作
    })
  );
});

function requestBackend(event) {
  // 请求备份操作
  var url = event.request.clone();
  return fetch(url).then(function(res) {
    // 请求线上资源
    //if not a valid response send the error
    if (!res || res.status !== 200 || res.type !== "basic") {
      return res;
    }

    var response = res.clone();

    caches.open(CACHE_VERSION).then(function(cache) {
      // 缓存从线上获取的资源
      cache.put(event.request, response);
    });
    return res;
  });
}

注意:service-worker.js 不能出现任何操作dom的事件,如果真想触发,可以通过postMessage事件让主程序执行。

3、本地调试的时候,要把registerServiceWorker.js文件中判断生成环境的代码去掉,方便我们测试,后期上线可以再还原,npm run serve运行。

打开Chrome开发者工具,先情况所有缓存,然后刷新页面,发现Cache Storage存放了值

20200929134551.jpg

4、几次刷新的情况下,我们发现,页面资源都通过ServiceWorer缓存抓取了

20200929134643.jpg

5、然后我们关闭网络如下图,再次刷新,页面依旧能够访问。实现了离线缓存的方式。

1601358474219557301.jpg


拓展

ServiceWorer是PWA核心功能之一,PWA有还有消息推送等其他模拟Native APP的功能。我们在做离线缓存时要考虑项目的必要性,如果很多资源都是需要后端接口返回的实时数据,不建议离线缓存。而且要控制好缓存的机制,不然上线后发现页面没有更新。


0

发表评论

评论列表(0)

  • 暂时没有留言
热门