Service Worker 圖片載入失敗處理

weixin_34162695發表於2019-01-16

Service Worker 圖片載入失敗處理

參考文件

git clone https://gitee.com/wjj0720/Service-Worker.git
執行 npm i 
npm start
訪問 http://127.0.0.1:3000/pages/index.html  

開啟控制檯  重新整理(由於demo做的是後載入 需重新整理後 看效果)

ctrl + c 結束node服務 再次重新整理頁面(從快取裡面讀取 依然顯示頁面)

複製程式碼

簡介

  • 背景

    有一個困擾 web 使用者多年的難題——丟失網路連線。之前的嘗試 — AppCache — 看起來是個不錯的方法,但是,它假定你使用時會遵循諸多規則,如果你不嚴格遵循這些規則,它會把你的APP搞得一團糟。Service worker 最終要去解決這些問題。雖然 Service Worker 的語法比 AppCache 更加複雜,但是你可以使用 JavaScript 更加精細地控制 AppCache 的靜默行為。有了它,你可以解決目前離線應用的問題,同時也可以做更多的事。 Service Worker 可以使你的應用先訪問本地快取資源,所以在離線狀態時,在沒有通過網路接收到更多的資料前,仍可以提供基本的功能(一般稱之為 Offline First)。這是原生APP 本來就支援的功能,這也是相比於 web app,原生 app 更受青睞的主要原因。

  • 什麼是Service Worker ?

    Service Worker是瀏覽器在後臺啟動的一條服務Worker執行緒

  • 功能和特性:

      1. 一個獨立的 worker 執行緒,且只有一個。
      2. 一旦被 install,就永遠存在,除非被 uninstall
      3. 需要的時候可以直接喚醒,不需要的時候自動睡眠(此處有坑)
      4. 可代理請求和返回,快取檔案,快取的檔案可以被網頁程式取到
      5. 能向客戶端推送訊息
      6. 不能直接操作 DOM
      7. 出於安全的考慮,必須在 HTTPS/localhost  環境下才能工作
      8. 非同步實現,內部大都是通過 Promise 實現
      9. 基於[web worker](http://www.ruanyifeng.com/blog/2018/07/web-worker.html)
    複製程式碼

使用

  1. 註冊
    // 相容判斷
   if ("serviceWorker" in navigator) {
    // 一般考慮載入問題 windoe.onload後載入
    window.addEventListener("load", function() {
      // scope 引數是選填的,可以被用來指定你想讓 service worker 控制的內容的子目錄
      navigator.serviceWorker.register("/sw.js", {scope: '/'})
        .then(function(registration) {
          // 註冊成功
          console.log( "ServiceWorker registration successful with scope: ", registration.scope )
        })
        .catch(function(err) {
          // 註冊失敗
          console.log("ServiceWorker registration failed: ", err)
        });
    })
  }
複製程式碼
  1. 使用 /sw.js
const precacheVersion = 2
const precacheName = "precache-v" + precacheVersion

var precacheFiles = [
  "/pages/index.html",
  "/images/dmx.jpg",
  "/images/broken.png"
]


/**新
*  SW.js 瀏覽器會自動檢查差異性 
* 發生變更 install 事件被觸發 此時,舊的 SW 還在工作,新的 SW 進入 waiting 狀態。
* 注意,此時並不存在替換接管,當你現在已經開啟的頁面關閉時,那麼舊的 SW 則會被 kill 掉。
* 新的 SW 就開始接管頁面的快取資源。 一旦新的 SW 接管,則會觸發 activate 事件。 
*/

self.addEventListener("install", e => {
  console.log("[ServiceWorker] Installed")
// skipWaiting() 方法跳過 waiting 狀態,然後會直接進入 activate 階段
  self.skipWaiting()

  e.waitUntil(
    caches.open(precacheName).then(cache => {
      // 如果其中有一個 載入失敗 那就代表著--這次啟動 GG  
      return cache.addAll(precacheFiles)

      // cache.put(request, response).then(function() {
      //   // 成功快取
      // });
    })
  ) 
})

self.addEventListener("activate", e => {
  console.log("[ServiceWorker] Activated")

  e.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(thisCacheName => {
          if ( thisCacheName.includes("precache") && thisCacheName !== precacheName ) {
            return caches.delete(thisCacheName)
          }
        })
      )
    })
  )

  // 更新客戶端
  // self.clients.claim()

})


// 監聽頁面的請求 (不僅僅是js請求)
self.addEventListener("fetch", e => {
  e.respondWith(
    caches.match(e.request).then(response => {
      // 有快取走快取
      if (response) {
        return response
      }

      return fetch(e.request) .then(fetchResponse => {

        // console.log('s-->', fetchResponse);
        if (fetchResponse.ok) return fetchResponse;
        // 載入失敗的情況下 入股是圖片 則返回預設圖片
        if (isImage(e.request)) {
          return returnBrokenImg()
        }

        
      }).catch(err => {
        if ( isImage(e.request) ) {
          return returnBrokenImg()
        }
      })
    })
  )
})

function isImage(fetchRequest) {
  return fetchRequest.method === "GET" && fetchRequest.destination === "image";
}

function returnBrokenImg () {
  return caches.match('/images/broken.png').then(response => response)
}


// 監聽頁面發來的訊息
self.addEventListener('message', function (message, e) {
  console.log('service接受到的資料--->', message, e);
  sendMessageToPage('嘟嘟嘟')
});

// 向頁面傳送訊息
function sendMessageToPage (msg) {

  self.clients.matchAll().then(function (clients) {
    if (clients && clients.length) {
      clients.forEach(function (client) {
        client.postMessage(msg);
      })
    }
  })
}

複製程式碼
  1. 客戶端更新

除了由瀏覽器觸發更新之外,如果24小時沒有更新,會強制更新。這意味著最壞情況下Service Worker會每天更新一次

  //localStorage 存下 版本  執行時候對比
  var version = 'precache-v3'
  navigator.serviceWorker.register('/sw.js').then(function (reg) {
    if (localStorage.getItem('sw_version') !== version) {
      reg.update().then(function () {
        localStorage.setItem('sw_version', version)
      });
    }
  })
複製程式碼
  1. 客戶端訊息
  // 監聽serviceWorker 訊息
  navigator.serviceWorker.addEventListener('message', function (event) {
      // 接受資料,
      console.log('頁面接受的資料:',  event);
  });
  // 傳送訊息
  document.getElementById('sendMSG').addEventListener('click', function () {
      console.log('繫結點選事件,點選後傳送資料');
      navigator.serviceWorker.controller.postMessage('嘀嘀嘀');
  });
複製程式碼
  1. 應用案例
  1. 攔截圖片載入失敗 返回預設圖片 案例 https://bitsofco.de/handling-broken-images-with-service-worker/
  2. 藍湖 https://lanhuapp.com/
複製程式碼

新鮮貨

  1. github.com/jiahaog/nat…
  2. imgcook.taobao.org/project

相關文章