service worker 學習

介紹

serviceWorker 能夠實現資源、網絡請求的緩存和處理,是 PWA 實現離線可訪問、穩定訪問、靜態資源緩存的一項重要技術javascript

特色

  • 必須運行在 https 協議下;爲了便於本地開發,localhost、127.0.0.1 這種非 HTTPS 協議也被瀏覽器認爲是安全源
  • Service Worker 是徹底異步實現的,內部的接口的異步化都是經過 Promise 實現,而且在 Service Worker 中不能直接操做 DOM。
  • 能夠攔截並代理請求,能夠處理請求的返回內容

使用

生命週期

sw生命週期

註冊

if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("./sw.js").then(function (registeration) {
    console.log(registeration);
  });
}

安裝、更新、攔截處理等操做

//  sw.js 文件
var VERSION = "v1";

// install 階段:安裝sw,以後請求資源,並將資源緩存在cacheStorage中
self.addEventListener("install", function (event) {
  event.waitUntil(
    caches.open(VERSION).then(function (cache) {
      return cache.addAll([
        "./start.html",
        "./static/jquery.min.js",
        "./static/mm1.jpg",
      ]);
    })
  );
});

// 激活階段:能夠作些 除舊迎新的工做
// self.clients.matchAll() 能夠獲取瀏覽器全部的標籤
self.addEventListener("activate", function (event) {
  event
    .waitUntil(
      caches.keys().then(function (cacheNames) {
        return Promise.all(
          cacheNames.map(function (cacheName) {
            // 若是當前版本和緩存版本不一致
            if (cacheName !== VERSION) {
              return caches.delete(cacheName);
            }
          })
        );
      })
    )
    .then(function (cache) {
      // 這裏能夠判斷若是cache裏原本沒有內容,表示第一次安裝,就不用通知用戶了
      return self.clients.matchAll().then(function (clients) {
        if (clients && clients.length) {
          clients.forEach(function (client) {
            // 給每一個已經打開的標籤都 postMessage
            client.postMessage("sw.update");
          });
        }
      });
    });
});

// 攔截fetch請求階段:捕獲請求並返回緩存數據
self.addEventListener("fetch", function (event) {
  event.respondWith(
    caches
      .match(event.request)
      .catch(function () {
        return fetch(event.request);
      })
      .then(function (response) {
        caches.open(VERSION).then(function (cache) {
          cache.put(event.request, response);
        });
        return response.clone();
      })
      .catch(function () {
        return caches.match("./static/mm1.jpg");
      })
  );
});

接收更新消息

navigator.serviceWorker.addEventListener("message", (event) => {
  console.log(event.data);
});

注意問題

做用域污染問題

做用域

scope 屬性控制當前 sw 的做用範圍
if ("serviceWorker" in navigator) {
  navigator.serviceWorker
    .register("./sw.js", { scope: "/demo" })
    .then(function (registration) {
      console.log(registration);
    });
}

// {scope:'/'}  表示做用在全部頁面
// navigator.serviceWorker.register('/foo/sw.js',{scope:'/'})  不會成功,由於sw 在foo路徑下,而想要做用的倒是全局;越界了

消除做用域污染

當一個項目,存在多個 sw 時,會出現 sw 彼此干擾的問題(好比一個資源在多個 sw 做用域中);能夠經過在註冊以前,先行 unregister 的方法來解決html

navigator.serviceWorker.getRegistration() 獲取頁面全部註冊的 serviceWorker
navigator.serviceWorker.getRegistration().then(function (regs) {
  for (var reg of regs) {
    reg.unregister();
  }
});

sw 觸發更新問題

serviceWorker 觸發更新的幾種方式

  • 瀏覽器每 24 小時自動更新一次 serviceWorker
  • 註冊新的 serviceWorker,帶上版本號,如 :/sw.js?v=201807021920
  • 手動更新 registeration.update()
  • 逐字節對比新的 sw 文件和舊的 sw 文件,有區別才更新

serviceWorker 更新過程理解

  • 開始更新前,老的 SW 會是激活狀態
  • 更新後新的 SW 會和老的 SW 共同存在,新的 SW 進入 install 生命週期
  • 若是新的 SW 沒有 install 成功,它將會被廢棄,老的 SW 繼續保持激活狀態
  • 一旦新 SW 安裝成功,它會進入 wait 狀態直到老的 SW 不控制任何 clients(即瀏覽器的標籤 tab)
  • self.skipwaiting()方法能夠跳過等待,讓新 SW 安裝成功後當即激活

上完線後,服務端接口是新的,sw.js 被瀏覽器緩存了怎麼辦

一、服務器端控制前端

location ~ \/sw\.js${
  add_header Cache-Control  no-store;
  add_header Pragma  no-cache;
}

二、前端使用版本控制java

// 這種方式不行,會觸發死循環
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/sw.js?v=" + Date.now());
}

使用 sw-register.js 文件jquery

// sw-register.js
if('serviceWorker' in navigator){
  navigator.serviceWorker.register('/sw.js?v=201807041262')
}
// index.html
<script>
  window.onload=function(){
    var script=document.createElement('script');
    var firstScript=this.document.getElementByTagName('script')[0];
    script.type="text/javascript";
    script.async=true;
    script.src="/sw-register.js?v="+Date.now();
    firstScript.parentNode.insertBefore(script,firstScript)
  }
</script>

serviceWorker 兜底方案

Q:若是 SW 運行過程當中,出現了問題怎麼辦?
A: 須要 找個能快速上線的開關 JS 文件 https://yourhost.com/switch.jsgit

默認 SW_FALLBACK=false ;緊急狀況 SW_FALLBACK=true;github

<script>
  window.onload=function(){
    var firstScript=document.getElementByTagName('script')[0];
    var fbScript=document.createElement("script");
    fbScript.type="text/javascript";
    fbScript.async=true;
    fbScript.src="https://yourhost.com/switch.js?v="+Date.now();
    firstScript.parentNode.insertBefore(fbScript,firstScript);

    fbScript.onload=function(){
      if('serviceWorker' in navigator && window.SW_FALLBACK){
        //getRegistration 的參數爲sw的scope值
        navigator.serviceWorker.getRegistration('/').then(function(reg){
          reg && reg.unregister();
        })
      }
    }
  }
</script>

經常使用 API 彙總

sw腦圖

serviceWorker 在瀏覽器調試工具中使用

google devtoolsweb

綜述

sw 應用場景不少,好比後端消息推送等等,由於時間緣由,沒有詳細瞭解這塊,能夠參考這篇
使用 Service Worker 發送 Push 推送chrome

總體文章,可能會有些問題,若有疑問,歡迎留言或者微信交流,共同窗習進步!後端

微信

參考

ServiceWorker MDN
Service Worker 最佳實踐
Service Worker 應用
service-worker
藉助 Service Worker 和 cacheStorage 緩存及離線開發

相關文章
相關標籤/搜索