一篇文章讓你了解 service-worker

WilburBSA 7年前發布 | 15K 次閱讀 瀏覽器 前端技術

Service Worker 是什么?

service worker 是獨立于當前頁面的一段運行在瀏覽器后臺進程里的腳本。

service worker不需要用戶打開 web 頁面,也不需要其他交互,異步地運行在一個完全獨立的上下文環境,不會對主線程造成阻塞。基于service worker可以實現消息推送,靜默更新以及地理圍欄等服務。

service worker提供一種漸進增強的特性,使用特性檢測來漸漸增強,不會在老舊的不支持 service workers 的瀏覽器中產生影響。可以通過service workers解決讓應用程序能夠離線工作,讓存儲數據在離線時使用的問題。

注意事項:

1.service worker運行在它們自己的完全獨立異步的全局上下文中,也就是說它們有自己的容器。

2.service worker沒有直接操作DOM的權限,但是可以通過postMessage方法來與Web頁面通信,讓頁面操作DOM。

3.service worker是一個可編程的網絡代理,允許開發者控制頁面上處理的網絡請求。

4.瀏覽器可能隨時回收service worker,在不被使用的時候,它會自己終止,而當它再次被用到的時候,會被重新激活。

5.service worker的生命周期是由事件驅動的而不是通過Client。

Service Worker生命周期

service worker擁有一個完全獨立于Web頁面的生命周期

  1. 注冊service worker,在網頁上生效

  2. 安裝成功,激活 或者 安裝失敗(下次加載會嘗試重新安裝)

  3. 激活后,在sw的作用域下作用所有的頁面,首次控制sw不會生效,下次加載頁面才會生效。

  4. sw作用頁面后,處理fetch(網絡請求)和message(頁面消息)事件 或者 被終止(節省內存)。

Service Worker支持使用

瀏覽器支持

service worker support

polyfill

使用 ServiceWorker cache polyfill 讓舊版本瀏覽器支持 ServiceWorker cache API,

https

Server需要支持https

通過service worker可以劫持連接,偽造和過濾響應,為了避免這些問題,只能在HTTPS的網頁上注冊service workers,防止加載service worker的時候不被壞人篡改。

Github Pages是HTTPS的,可以通過Github做一些嘗試

調試工具

在調試的時候可以用于unregister、stop、start等操作

chrome訪問 chrome://inspect/#service-workers 或 chrome://serviceworker-internals 查看service-workers

firefox通過 about:debugging#workers 查看

離線存儲數據

對URL尋址資源,使用 Cache API 。對其他數據,使用IndexedDB。

離線閱讀

demo

使用 https 訪問本文,打開ChromeDevTools,選擇Application選項卡->Service Workers

可以看到Service Workers注冊

點擊下面離線保存按鈕

然后選擇Cache Storage,可以看到文字內容已經緩存到Cache Storage

然后選擇Service Workers 勾選 Offline,NetWork出現了:warning:?,然后試試離線訪問本文:sunglasses:

原理

注冊 service worker

創建一個 JavaScript 文件(比如:sw.js)作為 service worker

告訴瀏覽器注冊這個JavaScript文件為service worker,檢查service worker API是否可用,如果可用就注冊service worker

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function(registration) {
    // Registration was successful
    console.log('ServiceWorker registration successful with scope: ',    registration.scope);
  }).catch(function(err) {
    // registration failed :(
    console.log('ServiceWorker registration failed: ', err);
  });
}

sw.js文件被放在這個域的根目錄下,和網站同源。這個service work將會收到這個域下的所有fetch事件。

如果將service worker文件注冊為/example/sw.js,那么,service worker只能收到/example/路徑下的fetch事件(例如: /example/page1/, /example/page2/)。

// Service Workers
if ('serviceWorker' in navigator && navigator.onLine) {
  navigator.serviceWorker.register('/sw.js').then(function(registration) {
    console.log('ServiceWorker registration successful with scope: ',    registration.scope);
  }).catch(function(err) {
    console.log('ServiceWorker registration failed: ', err);
  });

var currentPath = window.location.pathname; var cacheButton = document.querySelector('.offline-btn');

var imageArray = document.querySelectorAll('img'); if(cacheButton) { cacheButton.addEventListener('click', function(event) { event.preventDefault(); // 緩存當前鏈接和使用的圖片 var pageResources = [currentPath]; for (i = 0; i < imageArray.length; i++) { pageResources.push(imageArray[i].src); } caches.open('offline-' + currentPath).then(function(cache) { var updateCache = cache.addAll(pageResources);

    updateCache.then(function() {
      console.log('Article is now available offline.');
      cacheButton.innerHTML = "?";
    });

    updateCache.catch(function (error) {
      console.log('Article could not be saved offline.');
      cacheButton.innerHTML = "?";
    });
  });
});

} }</code></pre>

緩存站點的資源

定義需要緩存的文件,然后在sw注冊安裝后回到cache Api將資源文件寫入緩存。如果所有的文件都被緩存成功了,那么service worker就安裝成功了。如果任何一個文件下載失敗,那么安裝步驟就會失敗。

var cacheName = 'v1';
var assetsToCache = [
  '/styles/main.css',
  '/script/main.js'
];

self.addEventListener('install', function(event) { event.waitUntil( caches.open(cacheName).then(function(cache) { return cache.addAll(assetsToCache); }).then(function() { return self.skipWaiting(); }) ); });</code></pre>

從緩存中加載

service worker成功注冊,并且用戶瀏覽了另一個頁面或者刷新了當前的頁面,service worker將開始接收到fetch事件。

攔截網絡請求并使用緩存,緩存命中,返回緩存資源,否則返回一個實時從網絡請求fetch的結果。

self.addEventListener('fetch', function(event) {
  var requestUrl = new URL(event.request.url);
  if (requestUrl.origin === location.origin) {
      if (requestUrl.pathname === '/') {
      event.respondWith(
        caches.open(cacheName).then(function(cache) {
          return fetch(event.request).then(function(networkResponse) {
            cache.put(event.request, networkResponse.clone());
            return networkResponse;
          }).catch(function() {
            return cache.match(event.request);
          });
        })
      );
    }
  }

event.respondWith( caches.match(event.request).then(function(response) { return response || fetch(event.request); }) ); });</code></pre>

緩存版本管理

版本修改的時候會觸發activate,將舊版本的緩存清理掉。

var OFFLINE_PREFIX = 'offline-';
var CACHE_NAME = 'main_v1.0.0';
self.addEventListener('activate', function(event) {
  var mainCache = [CACHE_NAME];
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if ( mainCache.indexOf(cacheName) === -1 && cacheName.indexOf(OFFLINE_PREFIX) === -1 ) {
            // When it doesn't match any condition, delete it.
            console.info('SW: deleting ' + cacheName);
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
  return self.clients.claim();
});

Service Worker 庫

參考

 

來自:http://kailian.github.io/2017/03/01/service-worker

 

 本文由用戶 WilburBSA 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!