關於Service Worker
Service Worker
含義
Service Worker 首先是一個執行在後臺的 Worker 執行緒,然後它會長期執行,充當一個服務,很適合那些不需要網頁或使用者互動的功能。它的最常見用途就是攔截和處理網路請求。
Service Worker 是一個後臺執行的指令碼,充當一個代理伺服器,攔截使用者發出的網路請求,比如載入指令碼和圖片。Service Worker 可以修改使用者的請求,或者直接向用戶發出迴應,不用聯絡伺服器,這使得使用者可以在離線情況下使用網路應用。它還可以在本地快取資原始檔,直接從快取載入檔案,因此可以加快訪問速度。
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
-
`navigator.serviceWorker.register('/service-worker.js');`
});
}
上面程式碼確認瀏覽器支援 Service Worker 以後,會註冊一個 Service Worker。
為了節省記憶體,Service worker 在不使用的時候是休眠的。它也不會儲存資料,所以重新啟動的時候,為了拿到資料,最好把資料放在 IndexedDb 裡面。
Service Worker 是事件驅動的。
下面是攔截請求的例子。
self.addEventListener('fetch', (event) => {
event.waitUntil(
-
`if (event.request.url.includes('/product') {`
-
`let productId = event.data.productId`
-
`let productCount = getProductData(productId)`
-
`indexedDB.open('store', 1, (db) => {`
-
`let productStore = db.createObjectStore('products', { keyPath: 'id' })`
-
`productStore.put({ id: productId, count: ++productCount })`
-
`})`
-
`})`
)
})
Service Worker 不能直接操作 DOM。
使用步驟
登記
使用 service worker 的第一步,就是告訴瀏覽器,需要註冊一個 service worker 指令碼。
navigator.serviceWorker.register('sw.js'.then(() => {
console.info('註冊成功')
}).catch((err) => {
console.error('註冊失敗')
})
上面程式碼的sw.js
就是需要瀏覽器註冊的 service worker 指令碼。注意,這個指令碼必須與當前網址同域,service worker 不支援跨與指令碼。另外,sw.js
必須是從 HTTPS 協議載入的。
預設情況下,Service worker 只對根目錄/
生效,如果要改變生效範圍,可以執行下面的程式碼。
navigator.serviceWorker.register(
'/service-worker.js',
{ scope: '/products/fashion' }
)
安裝
一旦登記成功,接下來都是 service worker 指令碼的工作。下面的程式碼都是寫在 service worker 腳本里面的。
登記後,就會觸發install
事件。service worker 指令碼需要監聽這個事件。
-
self.addEventListener('install', event => {
-
event.waitUntil(() => console.info('安裝完成'))
-
})
event.waitUntil()
方法為事件完成後指定回撥函式。
self.addEventListener('install', (event) => {
-
`let CACHE_NAME = 'xyz-cache'`
-
`let urlsToCache = [`
-
`'/',`
-
`'/styles/main.css',`
-
`'/scripts/bundle.js'`
-
`]`
-
`event.waitUntil(`
-
`caches.open(CACHE_NAME)`
-
`.then (cache => cache.addAll(urlsToCache))`
-
`)`
})
啟用
安裝完成後,service worker 就會等待啟用。
-
self.addEventListener('activate', (event) => {
-
`let cacheWhitelist = ['products-v2']`
-
`event.waitUntil(`
-
`caches.keys().then (cacheNames => {`
-
`return Promise.all(`
-
`cacheNames.map( cacheName => {`
-
`if (cacheWhitelist.indexOf(cacheName) === -1) {`
-
`return caches.delete(cacheName)`
-
`}`
-
`})`
-
`)`
-
`})`
-
`)`
-
})
Service Worker 與網頁的通訊
self.addEventListener('activate', (event) => {
-
`event.waitUntil(`
-
`self.clients.matchAll().then ( (client) => {`
-
`client.postMessage({`
-
`msg: 'Hey, from service worker! I\'m listening to your fetch requests.',`
-
`source: 'service-worker'`
-
`})`
-
`})`
-
`)`
})
上面程式碼中,Service Worker 監聽activate
事件,然後向客戶端傳送一條資訊。
客戶端需要部署訊息監聽程式碼。
this.addEventListener('message', (data) => {
-
`if (data.source == 'service-worker') {`
-
`console.log(data.msg)`
-
`}`
})