編寫 Service Worker 指令碼

本程式碼研究室是 Google Developers 培訓小組所開發的「開發漸進式網路應用程式」訓練課程。這個課程將逐步介紹程式碼研究室,讓您充分發揮本課程的效益。

如需本課程的完整詳細資料,請參閱開發漸進式網路應用程式總覽

引言

本研究室將逐步引導您建立簡易的服務工作站,並說明服務工作站的生命週期。

課程內容

  • 建立、安裝基本的 Service Script 指令碼並安裝,並執行簡單的偵錯程序

注意事項

  • 基本 JavaScript 和 HTML
  • ES2015 Promises 的概念和基本語法
  • 如何啟用開發人員控制台

事前準備

從 GitHub 下載或複製 pwa-training-labs 存放區,並視需求安裝 Node.js LTS 版

瀏覽至 service-worker-lab/app/ 目錄,啟動本機開發伺服器:

cd service-worker-lab/app
npm install
node server.js

您隨時可以透過 Ctrl-c 終止伺服器。

開啟瀏覽器並前往 localhost:8081/

注意:請取消註冊所有 Service Worker,並清除 localhost 的所有服務工作站快取,以免它們幹擾研究室。在 Chrome 開發人員工具中,您可以在「應用程式」分頁的「清除儲存空間」部分中按一下 [清除網站資料],藉此清除設定檔資料。

使用您偏好的文字編輯器開啟「service-worker-lab/app/」資料夾。您要建立研究室的 app/ 資料夾。

這個資料夾包含以下內容:

  • below/another.htmljs/another.jsjs/other.jsother.html 是我們用來對服務工作者範圍進行實驗的範例資源
  • styles/ 資料夾包含此研究室的層層樣式表
  • test/」資料夾內含用來測試進度的檔案
  • index.html」是範例網站/應用程式的主要 HTML 網頁
  • service-worker.js 是用於建立 Service Worker 的 JavaScript 檔案
  • package.jsonpackage-lock.json 會追蹤這項專案中使用的節點套件
  • server.js」是代管快速伺服器,用來代管應用程式

在文字編輯器中開啟service-worker.js。請注意,檔案是空的。尚未在服務工作站中執行任何程式碼。

在文字編輯器中開啟index.html

<script> 標記中,加入下列程式碼來註冊 Service Worker:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('Service Worker is registered', registration);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

儲存指令碼,然後重新整理網頁。控制台應傳回訊息,指出服務工作者已註冊。在 Chrome 中,您可以開啟開發人員工具 (在 Windows 和 Linux 中按下 Ctrl + Shift + I,或在 Mac 上按下 ⌘ + Alt + I),按一下 [應用程式] 標籤,再按一下 [服務業者] 選項,即可查看服務業者是否已註冊。您應該會看到如下的內容:

選用:在不支援的瀏覽器中開啟網站,並確認支援檢查條件是否正常運作。

說明

上述程式碼會將 service-worker.js 檔案註冊為服務工作站。首先,檢查瀏覽器是否支援服務業者。某些瀏覽器可能不支援 Service Worker,因此當您每次註冊 Service Worker 時,請務必執行這項操作。接著,程式碼會使用 ServiceWorkerContainer APIregister 方法註冊服務業者,而該視窗位於視窗的 Navigator 介面中。

navigator.serviceWorker.register(...) 會在服務工作站成功註冊後傳回「承諾使用」來解析 registration 物件。如果無法註冊,承諾使用合約將遭到拒絕。

Service Worker 狀態中的變更會觸發 Service Worker 事件。

新增事件接聽程式

在文字編輯器中開啟service-worker.js

將下列事件監聽器新增至服務工作站:

self.addEventListener('install', event => {
  console.log('Service worker installing...');
  // Add a call to skipWaiting here
});

self.addEventListener('activate', event => {
  console.log('Service worker activating...');
});

儲存檔案。

手動取消註冊 Service Worker 並重新整理頁面,以安裝並啟用啟用的服務工作站。主控台記錄應該會指出新的服務工作站已註冊、安裝並啟用。

注意:註冊紀錄可能會與其他記錄 (安裝與啟用) 不符。Service Service Worker 會與頁面同時執行,因此無法保證記錄的順序 (註冊記錄來自網頁,安裝/啟用記錄則來自服務工作站)。不過,安裝服務、啟用和其他 Service Worker 事件都是在服務工作站內定義的順序發生,應一律按照預期的順序顯示。

說明

Service Worker 會在註冊結束時發出 install 事件。在上述程式碼中,系統會將訊息記錄在 install 事件監聽器中,但在實際應用程式中,這是快取靜態資產的絕佳地點。

註冊 Service Worker 時,瀏覽器會偵測服務業者是否為新的 (可能與先前安裝的 Service Worker 不同,或是這個網站沒有已註冊的 Service Worker)。如果 Service Worker 為新服務 (在這類情況下),瀏覽器會予以安裝。

Service Worker 會在控制網頁時發出 activate 事件。上述程式碼會在這裡記錄一個訊息,但此事件通常是用來更新快取。

在指定的範圍內,一次只能啟動一個服務工作站 (請參閱「探索服務工作站」範圍),因此在現有服務工作站停止使用之前,系統不會啟用新安裝的服務工作站。因此,必須先關閉服務業者控制的所有網頁,才能由新服務人員接管。由於我們已取消註冊現有的 Service Worker,因此會立即啟用新的 Service Worker。

注意:如果只是重新整理網頁不足以將控制權限轉移到新的 Service Worker,由於系統會要求新網頁在目前網頁載入前就要求新網頁,因此無意使用舊服務工作站。

注意:您也可以使用部分瀏覽器手動啟用新服務工作站,並使用開發人員工具和 skipWaiting() (如第 3.4 節所述)。

更新 Service Worker

service-worker.js 的任何位置新增以下註解:

// I'm a new service worker

儲存檔案並重新整理頁面。查看主控台中的記錄;請注意,新的 Service Worker 會安裝但未啟動。在 Chrome 中,您可以在開發人員工具的 [應用程式] 分頁中查看等待服務工作站。

關閉所有與 Service Worker 相關的頁面。然後重新開啟 localhost:8081/。主控台記錄應該會顯示新的服務工作站現已啟用。

注意:如果系統提供非預期的結果,請確認您的開發人員工具已停用 HTTP 快取。

說明

瀏覽器偵測到新服務和現有服務工作站檔案之間的位元組差異 (因為已新增註解),因此已安裝新的 Service Worker。由於一次只能對一位服務工作人員啟用服務 (即使在指定範圍內工作),在新的服務工作人員已經安裝之前,系統不會啟用該服務。關閉舊服務工作者之下的所有頁面後,我們就能啟用新的 Service Worker。

略過等候階段

即使有現有的服務工作人員,也可以略過等候階段,立即啟用新的 Service Worker 服務。

service-worker.js 中,在 install 事件監聽器中呼叫 skipWaiting

self.skipWaiting();

儲存檔案並重新整理頁面。請注意,即使先前的 Service Worker 受到控管,新的 Service Worker 也會立即安裝並啟用。

說明

skipWaiting() 方法可讓服務工作站在安裝完成後立即啟用。安裝事件監聽器是放置 skipWaiting() 呼叫的常見位置,但可以在等候階段或之前的任何階段呼叫該事件。如要進一步瞭解使用 skipWaiting() 的時機和方式,請參閱這份說明文件。現在,在研究室的其餘部分,我們現在可以測試新的服務工作站程式碼,而無需手動取消註冊服務工作站。

瞭解詳情

Service Worker 可做為網路應用程式與網路之間的 Proxy。

我們來新增擷取監聽器,以攔截來自我們網域的要求。

將下列程式碼加進 service-worker.js

self.addEventListener('fetch', event => {
  console.log('Fetching:', event.request.url);
});

儲存指令碼並重新整理頁面,以安裝並啟用啟用的服務工作站。

查看主控台並發現系統並未記錄任何擷取事件。請重新整理頁面,然後再次查看主控台。這次應會顯示網頁及其資產 (例如 CSS) 的擷取事件。

請點選 [其他網頁]、[其他頁面] 和 [返回]

您就會在主控台中看到每個網頁及其資產的擷取事件。所有記錄是否合理?

注意:如果您造訪網頁時並未啟用 HTTP 快取,系統可能會在本機快取 CSS 和 JavaScript 資產。如果發生這種情況,您將不會看到這些資源的擷取事件。

說明

在瀏覽器所屬的範圍內,瀏覽器發出的每個 HTTP 要求都會收到 Service Worker 的擷取事件。fetch event 物件包含要求。在服務工作站中監聽擷取事件的方式與監聽 DOM 的點擊事件類似。在我們的程式碼中,當擷取事件發生時,系統會將要求的網址記錄在控制台中 (實際上,我們也可以使用任意資源建立及傳回自己的自訂回應)。

為什麼系統沒有在第一次重新整理時記錄任何擷取事件?根據預設,除非網頁要求本身是透過服務業者處理,否則擷取網頁的事件並不會完成。這樣可確保網站的一致性,如果網頁在沒有服務工作站的情況下載入,子資源也會跟著載入。

瞭解詳情

解決方案程式碼

如要取得工作代碼的副本,請前往 04-intercepting-network-requests/ 資料夾。

Service Worker 具有「範圍」。Service Worker 的範圍決定服務工作站攔截路徑的路徑。

找出範圍

使用以下方式更新 index.html 中的註冊碼:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('SW registered with scope:', registration.scope);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

重新整理瀏覽器。請注意,控制台會顯示 Service Worker 的範圍 (在本例中為 http://localhost:8081/)。

說明

register() 傳回的承諾使用價格將解析為註冊物件,其中包含服務業者的範圍。

預設範圍是服務工作站檔案的路徑,並延伸至所有較低的目錄。因此,應用程式根目錄中的 Service Worker 會控制應用程式所有檔案的請求。

移動服務工作站

service-worker.js 移至 below/ 目錄,並更新 index.html 中註冊代碼中的 Service Worker 網址。

請在瀏覽器中取消註冊目前的服務工作站,然後重新整理頁面。

主控台顯示服務工作站的範圍現在是 http://localhost:8081/below/。在 Chrome 中,您也可以在開發人員工具的應用程式分頁中查看 Service Worker 範圍:

返回主頁面,按一下 [其他頁面]、[其他頁面] 和 [返回]。正在記錄哪些擷取要求?哪些?

說明

Service Worker 的預設範圍是 Service Worker 檔案的路徑。由於 Service Worker 檔案現在位於 below/,因此屬於其範圍。控制台目前只會記錄 another.htmlanother.cssanother.js 的擷取事件,因為這些是服務工作站唯一的資源。

設定任意範圍

將服務工作站移回專案根目錄 (app/),並在 index.html 的註冊碼中更新服務業者網址。

使用 MDN 參考資料,使用 register() 中的選用參數將服務工作站的範圍設為 below/ 目錄。

取消註冊 Service Worker 並重新整理頁面。按一下 [其他頁面]、[其他頁面] 和 [返回]

此外,主控台顯示服務工作站的範圍現在是 http://localhost:8081/below/,而且只會記錄 another.htmlanother.cssanother.js 的事件。

說明

註冊時,您可以透過傳遞其他參數來設定任意範圍,例如:

navigator.serviceWorker.register('/service-worker.js', {
  scope: '/kitten/'
});

在上述範例中,服務工作站的範圍設定為 /kitten/。Service Worker 會攔截來自 /kitten//kitten/lower/ 網頁的要求,而不會處理來自 /kitten/ 等網頁的要求。

注意:您不能設定超出服務工作站實際位置的任意範圍。不過,如果您的伺服器工作站已在使用 Service-Worker-Allowed 標頭運作的用戶端上運作,您可以在服務工作站的位置上方指定服務工作站的最大範圍。

瞭解詳情

解決方案程式碼

如要取得工作代碼的副本,請前往 solution/ 資料夾。

現在您有一個簡單的服務業者,可以開始執行和瞭解服務工作站的生命週期。

瞭解詳情

服務工作站生命週期

如要查看 PWA 訓練課程的所有程式碼研究室課程,請參閱歡迎程式碼研究室課程。