Service Workers簡介(二)

——evilrescuer翻譯自Matt Gaunt
寫的[Service Workers: an Introduction](收錄于 谷歌開發(fā)者)(https://developers.google.com/web/fundamentals/primers/service-workers/)

上一篇:Service Workers簡介(一)

一、緩存并返回請求

現(xiàn)在你已經(jīng)安裝了一個service worker,你可能想要返回一個自己緩存的響應(yīng),對吧?
在你安裝完service worker,用戶導(dǎo)航到另一個頁面或者刷新頁面,service worker 就開始接收fetch事件,例子如下:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});

這里,我們定義了我們的fetch事件,并在event.respondWith()里,我們從caches.match傳入了一個promise。這個方法從任何你的service worker緩存的文件中查找結(jié)果。
如果我們有一個匹配上的response,就返回緩存的值,否則就返回調(diào)用fetch的結(jié)果,也就是發(fā)起網(wǎng)絡(luò)請求,并返回收到的數(shù)據(jù)。
如果我們想累積緩存一個新的請求,可以處理fetch請求的response并把這個response加入緩存,就像下面這樣:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        return fetch(event.request).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have two streams.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});

我們做了這些:

  1. fetch 請求上,給 .then()增加一個 callback
  2. 一旦我們收到一個響應(yīng), 我們做這些檢查:
    1. 確認(rèn)響應(yīng)是合法的.
    2. 檢查狀態(tài)是 200.
    3. 確認(rèn)響應(yīng)類型是 basic(說明是從我們的源發(fā)起的請求),也就是不緩存第三方文件發(fā)起的請求的響應(yīng).
  3. 如果通過測試,我們 克隆 一個響應(yīng)。因為響應(yīng)是一個 ,響應(yīng)體只會被消耗一次。因為我們需要給瀏覽器返回響應(yīng),并且緩存起來以便將來使用, 所以我們需要克隆一下,一個用來發(fā)給瀏覽器,一個用來緩存起來。

二、更新一個service worker

有時你的service worker需要更新,此時你需要:

  1. 更新你的 service worker JavaScript 文件. 當(dāng)用戶來到你的網(wǎng)頁,瀏覽器嘗試重新下載這個JavaScript 文件。這個文件有任何改變,哪怕是一個字節(jié),都會被認(rèn)為是新的.
  2. 你的新 service worker 將會啟動,install的事件會被觸發(fā).
  3. 舊的service worker還在控制當(dāng)前頁面,所以新的service worker會進(jìn)入一個等待的狀態(tài)。
  4. 當(dāng)前打開的頁面如果被關(guān)閉,舊的 service worker會被銷毀,新的開始控制。
  5. 一旦新的service worker得到控制權(quán),它的activate事件會被觸發(fā).

我們常常在activate的callback中處理緩存管理。注意:如果你在安裝步驟清掉舊緩存,那些仍然有控制權(quán)的舊service worker,會立即停止那個緩存文件的工作,所以在這里做比較好。

比如說,我們有一個緩存叫 'my-site-cache-v1',我們希望把這個緩存分割成兩個,一個給頁面用,一個給博客文章用。那在install(安裝)步驟,我們創(chuàng)建兩個緩存:'pages-cache-v1' 和 'blog-posts-cache-v1' ,然后在activate(激活)步驟,我們刪掉我們的舊緩存'my-site-cache-v1'。

下面的代碼會循環(huán)查看 service worker 的緩存,刪掉那些不在白名單上的緩存。

self.addEventListener('activate', function(event) {

  var cacheWhitelist = ['pages-cache-v1', 'blog-posts-cache-v1'];

  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

三、粗糙的邊緣和陷阱

server worker是個新玩意兒。這里提一些問題,希望可以盡早被解決,但是現(xiàn)在還是需要注意一下。

1.安裝失敗的信息無法告知

如果你注冊了一個service worker,但是沒在chrome://inspect/#service-workers or chrome://serviceworker-internals中出現(xiàn),那可能是因為拋出錯誤,或者一個可被傳輸?shù)絜vent.waitUntil()的rejected promise錯誤

為了解決這個問題,前往 chrome://serviceworker-internals 并打開谷歌開發(fā)工具,對安裝事件進(jìn)行調(diào)試。 以及配合這篇文章提到的: 《調(diào)試未捕獲的異常》。

2.fetch的默認(rèn)情況
2.1 沒有credentials憑據(jù)

當(dāng)你使用fetch時,默認(rèn)情況下,請求是不帶credentials憑據(jù)的(比如cookies),如果你需要憑據(jù),可以這樣使用:

fetch(url, {
  credentials: 'include'
})

對于同源的URL來說,我們特意這么設(shè)置后, 會比 XHR復(fù)雜一些。fetch行為和其他的CORS 請求一樣, 比如 <img crossorigin>, 如果你不設(shè)置<img crossorigin="use-credentials">,它是不會發(fā)cookies的。

2.2 非CORS默認(rèn)安裝失敗

By default, fetching a resource from a third party URL will fail if it doesn't support CORS. You can add a no-CORS option to the Request to overcome this, although this will cause an 'opaque' response, which means you won't be able to tell if the response was successful or not.
默認(rèn)情況下,由于沒有CORS,從第三方URL獲取一個資源會失敗。你可以加上一個no-CORS,盡管這會導(dǎo)致一些不透明的響應(yīng),因為你不知道響應(yīng)是否成功。做法如下:

cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
  return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
  console.log('All resources have been fetched and cached.');
});

譯者注:如果你不了解no-CORS,請查看 request模式-MDN

2.3 處理響應(yīng)式圖像

對于service worker來說,如果你想在安裝步驟緩存一張圖片,你有幾種選擇:

  • 安裝所有可能用到的圖片
  • 緩存低分辨率版本圖片
  • 緩存高分辨率版本圖片

實際上你應(yīng)該選擇選項2或3,因為下載所有圖像會浪費(fèi)存儲空間。

假設(shè)您在安裝時選擇低分辨率版本,并且您希望在加載頁面時嘗試從網(wǎng)絡(luò)中檢索更高分辨率的圖像,但如果高分辨率圖像失敗,則回退到低分辨率版本。這看起來很好,但有一個問題。

比如我們有下面兩張圖片:

Screen Density  Width   Height
      1x         400      400
      2x         800      800

我們可能這么使用:

<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />

如果我們在2x的顯示器上,瀏覽器會去下載 image-2x.png,如果離線了,你可以用.catch() 緩存,并返回 image-src.png(如果已經(jīng)緩存上的話)。
但是瀏覽器會期望圖像考慮到2x屏幕上的額外像素,因此圖像將顯示為200x200 CSS像素而不是400x400 CSS像素。唯一的方法是在圖像上設(shè)置固定的高度和寬度。

四、學(xué)習(xí)更多

這里有一系列關(guān)于 service worker 的文檔: https://jakearchibald.github.io/isserviceworkerready/resources ,可能對你有用。

(完)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容