昨日我們過目Service Worker的生命週期後,今天第一步,在專案/public
,底下建立一個service-worker的js檔案。
檔案名稱可以隨自己開心沒有規定,接著,我們在/src/js
資療夾底下,建立app.js
,提供index.html
中使用的JS檔案。
if('serviceWorker' in navigator){
navigator.serviceWorker
.register('/service-worker.js')
.then(function(){
console.log('Service Worker 註冊成功');
}).catch(function(error) {
console.log('Service worker 註冊失敗:', error);
});
} else {
console.log('瀏覽器不支援');
}
接著打開app.js
輸入上面這段程式,就完成註冊了,一整個很簡單。
解讀起來就是詢問瀏覽器(navigator
)中是否存在(in
)名為「serviceWorker
」的物件存在,如果瀏覽器支援Service Worker
,就會進入「navigator.serviceWorker.register(檔案路徑)」,檔案路徑「/檔案名稱」,/
是確保路徑都是從root
開始看,從專案的架構,就是指/public/
底下的範圍。
service worker
的檔案放置的位置會影響,能掌管的範圍。
舉例來說,預設的掌管範圍放在根目錄底下的service worker
,
可以存取到的範圍為整個網站:
如果檔案存放在「/public/Product/service-worker.js」,
能控制的範圍為Produc資料夾範圍內的頁面:
透過scope
參數,可以加限制存取範圍
//假設這行是在「/Product/Detail.html」頁面執行
navigator.serviceWorker.register('/sw.js', {scope: './'})
「./」依照註冊Service Worker
的頁面索在資料夾為基準,意思就是只能存取「/Product/」底下的所有資源。
Service Worker
回傳會promise
,關於promise
會再後面幾篇中再做介紹。
在網頁載入的時候,先不考慮Service Worker
對於開發者最注重的就是畫面資料的顯示速度,而今天就算我們網站有使用Service Worker
,在使用者感受上,他並不會有任何感受,因為都是運行在背景上。
而假如今天我們網頁有很多圖片、CSS和JS資源要載入,是必我們就必須分配CPU和記憶體來執行,雖然現在手機規格越來越好,但我們還是必須假設頻寬是有一定限制,既然Service Worker
對使用者感受上不會有任何反應,我們等頁面資源全部載入後再註冊也不遲。
因此,就將上面的註冊方法改寫了一下,讓資源等待網頁載入後,再註冊Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js');
});
}
但是否該同時載入,還是依照情境再做決定,並沒有強制說一定要使用哪一個。
在app.js
註冊Service Worker
後,可以透過install
事件,來捕捉網頁上有沒有成功註冊Service Worker
。
Service Worker
只會註冊一次,當更新了Service Worker
的檔案後,重新瀏覽頁面會被視為新的Service Worker
。self.addEventListener('install', function(event){
console.log('[SW] 安裝(Install) Service Worker!',event);
});
self
是一個語法糖,讓我們可以存取Service Worker
的背景程式,有寫過網頁的應該都覺得addEventListener
這個關鍵字很熟悉,但是在Service Worker
裡面,是無法使用click
之類我們平常所使用的事件,因為再前一天說過,Service Worker
是一套運行再背景的程式,是沒有權限能存取DOM的,所以理所當然能操作DOM的事件在這邊都是無法使用的。
接著我們運行網站後,開F12/Console視窗
,可以看到成功監聽到註冊的訊息,代表成功抓到Service Worker的檔案了。
再安裝完之後,Service Worker
就可以執行功能性的需求(如:推播)。
self.addEventListener('activate', function(event){
console.log('[SW] 觸發(Activate) Service Worker!',event);
return self.clients.claim();
});
接著新增監聽activate
,clients.claim()
,是一個控制住,不受控的資源的方法,看官網用了一個Demo介紹了這個方法。
它的意思是,當你打開頁面的時候一開始你會註冊Service Worker
,但是你並還沒有接收到圖檔的需求,因為設定3秒的Timeout才發出dog.svg的需求,所以需求並沒有經過Service Worker
,這時候你再f5刷新畫面一次,觸發Fetch
事件的時候,頁面和圖檔就經過Service Worker
的時候,原本是狗的圖就被替換成貓了。
事件後執行,會發現console
視窗,只出現註冊及安裝的訊息。
Service Worker 註冊成功
[SW] 安裝(Install) Service Worker! InstallEvent {isTrusted: true, type: "install", target: ServiceWorkerGlobalScope, currentTarget: ServiceWorkerGlobalScope, eventPhase: 2, …}
那是因為假如我們瀏覽器上已經有一個Service Worker
已經註冊了,現在我們更新了新版的Service Worker
上去後,他會等待舊的Service Worker
終止後,才執行新的Service Worker
,為了方便Debug,Chrome在開發者工具(F12)上,有一個Application
區塊,有Service Workers
的狀態區,讓我們可以清楚現在網頁上的狀況。
如上圖,綠色#68401的點是我們第一次在測試install
事件時註冊的Service Worker
,
而橘色#68402是在加了activate
事件後的Service Worker
。
在Application/Service Workers
上,有三個方式可以解決這個問題。
Service Worker
),最右邊**skipWaiting
**的連結,它可以跳過等待時間直接強制執行新的Service Worker
。Update on reload
**打勾,意思是每次重整時,都重新註冊Service Worker
。如果在沒有使用第2-3點的情境下,你修改了新的Service Worker
,當前一次的localhost還掛在頁面上,再建置後,開啟新的localhost頁面時候,會發現舊的Service Worker一直掛在上面,那是因為有舊的分頁沒關掉造成的,如果要更新就要將全部localhost的分頁關掉再進入網站,才會載入新的Service Worker
。
關於Service Worker
install
事件是第一個會觸發的事件,而且再頁面上只會觸發一次。Fetch
不會通過Service Worker
,除非Service Worker
已經install
,且再一次刷新頁面才會有效果。clients.claim()
強制控制沒有控制到的資源。關於 Service Worker
更新時間
Service Worker
的頁面的時候。Service Worker
的程式有改變的時候,他會觸發install
事件,並進入activate
中等待,可以透過skipWaiting
強制更新。Service Worker
再執行install
的時候失敗,會被刪除,舊的Service Worker
仍然會繼續執行。Service Worker
安裝後,會處於等待狀態(wait
),當舊的Service Worker
沒有控制任何資源的時候,才會刪除舊的觸發新的。MDN:SW.register()
Clients.claim()
The Service Worker Lifecycle
https://github.com/DakHarry/30day-pwas-practice