service worker 對靜態資源進行快取

wuwei123發表於2020-03-29

service worker

HTML5標準中引入了webworker,它是執行在後臺的JavaScript,獨立於其他指令碼,不會影響頁面的效能。Service Worker就是Web Worker的一種實現,充當Web應用程式的本地代理伺服器;在特定路徑註冊Service Worker後,我們可以攔截並處理該路徑下的所有網路請求;本文中,我們就是藉助於這種代理機制,實現對web頁面核心資源可程式設計式快取。

基本用法

1. 註冊

navigator.serviceWorker.register('your_service_worker.js',{scope: 'your-path-name'}).then(function (registration) {
    console.log('succeeded');
}).catch(function (error) {
    console.log('error' + error);
});

複製程式碼

register 方法接受兩個引數,第一個是service worker 的檔案路徑,第二個是 service worker 的配置項,其中scope 屬性控制的是service worker 工作目錄,預設為 service worker 所在檔案目錄。在同一個Origin 下可以註冊多個Service Worker,但是Scope不能相同。

2. 登出

navigator.serviceWorker.getRegistration('your_service_worker.js',{scope: 'your-path-name'}).then(function (registration) {
    if (registration && registration.unregister) {
        registration.unregister().then(function (isUnRegistered) {
            if (isUnRegistered) {
                console.log('unRegistration  succeeded.');
            } else {
                console.log('unRegistration failed.');
            }
        });
    }
).catch(function (error) {
    console.log('[SW]: UnRegistration failed with. ' + error);
});
複製程式碼

首先需要通過getRegistration獲取service woker,然後呼叫service woker的unregister 方法進行登出。

3.事件監聽

// 安裝監聽
this.addEventListener('install', function (event) {
  console.log('Service Worker install');
});

// 啟用監聽
this.addEventListener('activate', function (event) {
  console.log('Service Worker activate');
});
複製程式碼

生命週期

生命週期

Installing

註冊完 service worker 後,瀏覽器會下載並解析,預設情況下,service worker會24小時被下載一次。

Activating & Activated

在指令碼被安裝完成後,service worker 會 依次進入 Activating 和 Activated 狀態。失敗則會進入Redundant 狀態。

Redundant

在 Installing 或 Activating 失敗後,會進入此狀態,在舊 service worker 被新 service Worker 取代後,也會進入此狀態。

通訊

從頁面到service worker

if ('serviceWorker' in window.navigator) {
  navigator.serviceWorker.register('your_service_worker.js', { scope:'your-path-name' })
    .then(function (reg) {
      navigator.serviceWorker.controller && navigator.serviceWorker.controller.postMessage("hello");
    });
}

複製程式碼

從 service worker 到頁面

首先監聽,來自頁面的訊息,根據頁面的訊息得到event.source 進行相應。

this.addEventListener('message', function (event) {
  event.source.postMessage('this message is from sw.js, to page');
});
複製程式碼

靜態資源快取

終於到了本文的重點了,在Service worker中可以快取的包括css、js、圖片等在內的幾乎全部靜態資源。使用service Worker 快取資源的方式一般為

1.service worker安裝成功後開始快取所需的資源
this.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open('my_sw_cache').then(function (cache) {
      return cache.addAll([
        '/',
        '/script/style.css',
      ])
    }
    ));
});

複製程式碼

使用 caches.open 方法新建或開啟一個已存在的快取,cache.addAll方法是請求指定連線的資源並把他們儲存到這個快取中,而使用event.waitUntil能保證資源被快取完成前 Service Worker 不會被安裝完成,避免發生錯誤。

2.監聽瀏覽器的所有fetch請求,對已經快取的資源使用本地快取進行返回。
this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        //該fetch請求已經快取
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});
複製程式碼

3. 版本控制

前面說到,有新的 service worker 後,舊版本的頁面會被全部關閉,此時就需要我們清理舊的快取了,如何識別並找出舊版本的快取就是重點了。而cacheStorage 提供了簡單的API,便於我們能查詢出舊的快取資源。其中CACHE_PREFIX是應用的cache字首,CACHE_VERSION是本次cache的版本號。

function deleteCache() {
    return caches.keys().then(function (keys) {
        var all = keys.map(function (key) {
            if (key.indexOf(CACHE_PREFIX) !== -1 && key.indexOf(CACHE_VERSION) === -1){
                  console.log('Delete success-->' + key);
                  return caches.delete(key);
            }
        });
        return Promise.all(all);
    });
}
複製程式碼

白名單控制

並不是所有的舊的快取都不需要,有些快取可以一直使用,所以需要設定一個白名單,當Service Worker 被啟用的時候,將不在白名單中的快取刪掉。

const noDelete = ['you_no_delete_source.js'] // 白名單
function deleteCache() {
    return caches.keys().then(function (keys) {
        var all = keys.map(function (key) {
            if (key.indexOf(CACHE_PREFIX) !== -1 && key.indexOf(CACHE_VERSION) === -1 && !noDelete.includes(key)){
                  console.log('Delete success-->' + key);
                  return caches.delete(key);
            }
        });
        return Promise.all(all);
    });
}
複製程式碼

總結

上述程式碼只是實現了一個demo,實際情況可能複雜多了,比如

1.哪些資源需要快取,並不是人為控制的,更多情況是webpack打包生成的檔案,所以需要生成一套正則匹配規則,用於識別不同的檔案,哪些需要快取,快取多久。

2.快取控制不得當,可能獲得災難性後果,若使用者得不到正確的相應結果,可能造成頁面無響應,資料錯亂各種錯誤。所以需要對響應錯誤碼做額外處理,如避免快取304,404,xx等不需要快取的內容。

相關文章