阅读 74

serviceWorker详解(Ⅰ)——通往和平的桥梁

serviceWorker是什么? 解决什么问题?

有一个困扰 web 用户多年的难题——丢失网络连接。即使是世界上最好的 web app,如果下载不了它,也是非常糟糕的体验。如今虽然已经有很多种技术去尝试着解决这一问题。而随着离线页面的出现,一些问题已经得到了解决。但是,最重要的问题是,仍然没有一个好的统筹机制对资源缓存和自定义的网络请求进行控制。

Service worker 最终要去解决这些问题。你可以使用 JavaScript 更加精细地控制 AppCache 的静默行为。有了它,你可以解决目前离线应用的问题,同时也可以做更多的事。 Service Worker 可以使你的应用先访问本地缓存资源,所以在离线状态时,在没有通过网络接收到更多的数据前,仍可以提供基本的功能(一般称之为 Offline First)。

什么情况(产品)中使用serviceWorker?

  1. webApp
  2. webview
  3. SPA
  4. 多页应用的局部优化,无数据敏感且不会频繁变化的区域

如何引入?

if ('serviceWorker' in navigator) { // 判断当前浏览器是否支持serviceWorker
   /* navigator.serviceWorker.register方法用于注册我们自己编写的serviceWorker控制逻辑,返回Promise    * 简单理解就是把编写serviceWorker控制逻辑的sw.js引入了
    * 第二个参数,scope指定serviceWorker可以控制的文件范围,这里是/sw-test/下的全部,默认不传也就是全部    */
  navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) {
    // registration worked
    console.log('Registration succeeded. Scope is ' + reg.scope);
  }).catch(function(error) {
    // registration failed
    console.log('Registration failed with ' + error);
  });
}
复制代码

serviceWorker的控制逻辑是怎样的?

类似于window的onload,serviceWorker也提供了一些生命周期的钩子。以下:

  • install
  • activate
  • message
下面我们通过一个img gallery的实例来初步了解serviceWorker的使用方式。在开始之前请认真阅读下面下环线的文字:

需要注意的是,serviceWorker独立于主线程之外,没有window全局对象,使用self表示当前运行环境的顶层。不能操作DOM。

首先是在我们的业务逻辑代码app.js(主入口)中,注册serviceWorker:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) {

    if(reg.installing) {
      console.log('Service worker installing');
    } else if(reg.waiting) {
      console.log('Service worker installed');
    } else if(reg.active) {
      console.log('Service worker active');
    }

  }).catch(function(error) {
    // registration failed
    console.log('Registration failed with ' + error);
  });
}
...
复制代码

serviceWorker控制逻辑sw.js

/*
* self表示当前正在运行的serviceWorker,这里监听install的结果
* event.waitUntil是serviceWorker event对象的一个特有方法,具体描述请参与MDN上ExtendableEvent.waitUntil()的解释* caches.open一系列方法是对本地缓存文件进行一些操作,具体会在下一期讲到
*/
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});

// fetch是在serviceWorker中对请求进行拦截,缓存有则从缓存中读取,没有则从服务端拿然后缓存到本地
self.addEventListener('fetch', function(event) {
  event.respondWith(caches.match(event.request).then(function(response) {
    // caches.match() always resolves
    // but in case of success response will have value
    if (response !== undefined) {
      return response;
    } else {
      return fetch(event.request).then(function (response) {
        // response may be used only once
        // we need to save clone to put one copy in cache
        // and serve second one
        let responseClone = response.clone();

        caches.open('v1').then(function (cache) {
          cache.put(event.request, responseClone);
        });
        return response;
      }).catch(function () {
        return caches.match('/sw-test/gallery/myLittleVader.jpg');
      });
    }
  }));
});
复制代码

以上,serviceWorker的使用流程就是如此。

下一期我们讲解serviceWorker的一些api使用。

本文使用的代码请参考sw-test

文章分类
前端
文章标签