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等不需要快取的內容。