可以拿來做cache 的另一個方式
在開啟網頁時,需要先確認該瀏覽器是否支持serviceWorker,如果支持,就可以依照 js 內容來使用serviceWorker。
register 時需要注意的是,serviceWorker 只能cache 自己目錄 或者 下層目錄 的內容,scope 可以在此時指定。
在此範例內的swFinal.js 有帶著版本號,這樣才能在更新檔案後立刻反應
test.html 內容
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
<script type="text/javascript">
if ( "serviceWorker" in navigator ) {
//1. 路徑:這個js路徑會影響能存取的範圍(該目錄&下層目錄),放根目錄為整站
//2. scope:放置js 檔案的路徑下的某個路徑才會cache.
// 預設 : navigator.serviceWorker.register( "swFinal.js" )
// 加上scope: navigator.serviceWorker.register( "swFinal.js",{scope: './'} ) //相當於沒寫
// 加上scope: navigator.serviceWorker.register( "swFinal.js",{scope: '/aaa/'} ) //放置js 檔案的目錄下的aaa資料夾下檔案
navigator.serviceWorker.register( "swFinal.js?version=1.02" ) //改版號就可以換sw,避免使用到原本sw 裡面檔案還是舊的
.then( function ( registration ) {
console.log( "ServiceWorker registration successful with scope: ", registration.scope );
const sw = registration.installing || registration.waiting || registration.active;
sw.postMessage({ milliseconds: Date.now() });
} ).catch( function ( err ) {
console.error( "ServiceWorker registration failed: ", err );
} );
}
</script>
</head>
<body>
My awesome PWA
</body>
</html>
swFinal.js 是要register 使用的檔案,內容包含了
- activate:啟動時,會自動呼叫的event,這邊會將所有cached 的文件做一次檢查,並剔除舊的檔案(非目前版本)
- install :在activate 之後會自動呼叫的event,我們在這邊add 所有需要cached 的檔案,另外也添加一個OFFLINE時使用的檔案
- fetch:取檔案用,如果cache內有該檔案,則直接取出使用,沒有在列表內也可以設定DYNAMIC_CACHE_LIMIT 來啟動動態cache 機制。當網頁伺服器壞掉,也會返回原本cached 的OFFLINE 頁面。
swFinal.js 內容
const VERSION = "1.02";
const DYNAMIC_CACHE_LIMIT = 0;
const CACHE_DYNAMIC_NAME = "dynamic-" + VERSION;
const CACHE_STATIC_NAME = "static-" + VERSION;
const STATIC_FILES = [
'/',
'/index.html?version=1.01',
'/testCache2.html?version=1.01',
'https://fonts.googleapis.com/css?family=Noto+Sans+TC&display=swap',
'https://fonts.googleapis.com/icon?family=Material+Icons',
'https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css'
];
const OFFLINE_URL = "/offline.html"; //這個是讓有跑過此service worker 預載後,網路斷了又要載入不在列表內的檔案時,預設載入此檔案
self.addEventListener('message', function(event) {
console.log('[Service Worker] Message Service Worker ...', event);
});
self.addEventListener('activate', function(event) {
console.log('[Service Worker] Activating Service Worker ...', event);
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if(key !== CACHE_STATIC_NAME && key !== CACHE_DYNAMIC_NAME) { //遍尋所有cache 刪除非目前版本的
console.log('[Service Worker] Removing old cache.', key);
return caches.delete(key);
}
}));
})
);
return self.clients.claim();
});
/* Service Worker Event Handlers */
self.addEventListener('install', function(event) {
console.log('[Service Worker] Installing Service Worker ...', event);
event.waitUntil(
caches.open(CACHE_STATIC_NAME).then(function(cache) {
console.log('[Service Worker] Precaching App Shell');
cache.addAll(STATIC_FILES); //這邊預載列表內的所有檔案到cache.
cache.add(OFFLINE_URL); //這邊預載列表內的所有檔案到cache.
})
);
self.skipWaiting(); //假設原本有另一個分頁開啟同一個sw,會直接蓋掉,而不是預設的waiting.
});
self.addEventListener('fetch', function(event) {
// Cache with Network Fallback
event.respondWith(
caches.match(event.request).then(function(response) {
if(response) { //在cache 內找到
return response; //回傳cache 內容
} else { //cache 內沒有,用fetch去抓並存起來
return fetch(event.request).then(function(res) {
return caches.open(CACHE_DYNAMIC_NAME).then(function(cache) { //動態的,存到動態的cache 分類中
if(DYNAMIC_CACHE_LIMIT > 0){
trimCache(CACHE_DYNAMIC_NAME, DYNAMIC_CACHE_LIMIT); //動態cache 的部分,加上數量限制
cache.put(event.request.url, res.clone()); //加入動態cache
}
return res;
})
}).catch(function(err) {
return caches.open(CACHE_STATIC_NAME).then(function(cache) { //fetch失敗(沒網路)
if(event.request.headers.get('accept').includes('text/html')) {
return cache.match(OFFLINE_URL); //返回預設的斷線畫面
}
});
});
}
})
);
});
function trimCache(cacheName, maxItems) {
caches.open(cacheName).then(function(cache) {
return cache.keys().then(function(keys) {
if(keys.length > maxItems) {
cache.delete(keys[0]).then(trimCache(cacheName, maxItems));
}
});
})
}
參考資料: