/

Service Workers教程

Service Workers教程

Service Workers是行動網頁上預先引進Web應用中的一項核心技術。它們允許資源的快取和推播通知,這兩個主要的特點早期只在本機應用程序中出現。

Service Workers介紹

Service Workers是漸進式Web應用的核心,因為它們允許資源的快取和推播通知,這是到目前為止區分本機應用程式的兩個主要特點之一。

Service Worker是您的網頁與網路之間的可編程代理,提供截取和快取網路請求的能力,有效地為您的應用程序創建了一個離線優先的體驗。

它是Web workers的一種特殊類型,是與Web頁面關聯的JavaScript文件,在工作上下文中運行在另一個線程上,不阻塞主線程,並且具有非阻塞的好處 - 因此可以進行計算而不會犧牲用戶界面的響應速度。

由於處於單獨的線程上,它沒有DOM訪問權限,也無法訪問本地存儲API和XHR API,只能使用通道消息API與主線程進行通信。

Service Workers與其他最新的Web API合作:

而且它們僅在HTTPS協議頁面上可用,除了不需要安全連接的本地請求以進行更簡單的測試。

背景處理

Service Workers獨立於它們相關的應用程序運行,當它們處於非活躍狀態時,它們可以接收消息。

例如,在以下情況下,它們可以運作:

  • 手機應用程式處於背景狀態,未活躍
  • 手機應用程式已關閉,所以即使在背景中也未運行
  • 瀏覽器已關閉,如果應用程式在瀏覽器中運行

Service Workers非常有用的主要情境有:

  • 它們可以被用作快取層來處理網路請求,並在離線時快取內容供使用
  • 允許推播通知

Service Worker只在需要時運行,並在不使用時停止運行。

離線支援

以往,Web應用的離線體驗非常差。沒有網路時,許多網頁移動應用程式將無法工作,而本機移動應用程式卻能提供正常運作的版本或某種令人愉悅的訊息。

這不是一個愉快的消息,但以下是Chrome在沒有網路連接的情況下的網頁外觀:

缺少Service Workers的離線支援

也許這唯一好玩的地方是通過點擊恐龍可以玩一場免費遊戲,但很快就會變得無聊。

恐龍遊戲Chrome

在最近的過去,HTML5 AppCache已經有望允許Web應用快取資源並離線工作,但由於其缺乏靈活性和令人困惑的行為,清楚地表明它不夠好,未能兌現它的承諾(並且已經停用)。

Service Workers是離線快取的新標準。

哪種類型的快取是可能的?

安裝期間進行預快取資源

可以在首次開啟應用時安裝應用中重複使用的資源,例如圖像、CSS和JavaScript文件。

這為被稱為App Shell架構的基礎提供了。

快取網路請求

使用Fetch API,我們可以編輯來自服務器的響應,確定服務器是否不可到達,並提供快取中的響應。

Service Worker生命週期

Service Worker需要經過3個步驟才能完全運作:

  • 註冊
  • 安裝
  • 啟動

註冊

註冊告訴瀏覽器服務器工作者的位置,並在後台開始安裝。

例子代碼將服務器工作者放在worker.js中進行了註冊:

1
2
3
4
5
6
7
8
9
10
11
12
13
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/worker.js')
.then((registration) => {
console.log('Service Worker registration completed with scope: ',
registration.scope)
}, (err) => {
console.log('Service Worker registration failed', err)
})
})
} else {
console.log('Service Workers not supported')
}

即使此代碼多次被調用,當服務工作者是新的且以前未被註冊,或者它已被更新時,瀏覽器只執行註冊。

範圍

register()調用還接受一個範圍參數,該參數是確定由服務工作者可以控制的應用程序的那一部分的路徑。

它默認為包含服務工作者文件的文件夾中的所有文件和子文件夾,因此如果將它放在根目錄中,它將對整個應用程序進行控制。如果將它放在子文件夾中,它只能控制該路徑下可訪問的頁面。

以下示例通過指定範圍為/notifications/文件夾註冊了該服務工作者。

1
2
3
navigator.serviceWorker.register('/worker.js', {
scope: '/notifications/'
})

/很重要:在這種情況下,/notifications頁面不會觸發Service Worker,而如果範圍為

1
2
3
{
scope: '/notifications'
}

則會找到。

注:服務工作者無法從文件夾“升級”自己:如果將其文件放在/notifications下,則無法控制/路徑或不在/notifications下的任何其他路徑。

安裝

如果瀏覽器確定服務工作者過時或以前從未註冊過,它將繼續安裝它。

1
2
3
self.addEventListener('install', (event) => {
//...
});

這是一個好的事件,可以通過初始化快取和使用Cache API快取App Shell和靜態資源來為Service Worker做好準備。

啟動

啟動階段是第三步,一旦服務工作者成功註冊和安裝,它就能夠在新的頁面加載時運作。

它無法與已經加載的頁面互動,這意味著服務工作者只在用戶第二次與應用程序交互或重新加載已經打開的頁面時有用。

1
2
3
self.addEventListener('activate', (event) => {
//...
});

這個事件的一個好的用例是清理舊的快取和在新版本中未使用的舊版本相關事項。

更新Service Worker

要更新Service Worker,只需要更改其中一個位元組,並在運行註冊代碼時進行更新。

一旦Service Worker被更新,直到所有使用舊服務器工作者加載的頁面關閉才會可用。

這確保了任何正在運行的應用程序/頁面上不會發生任何故障。

僅刷新頁面是不夠的,因為舊工作者仍在運行,並且尚未被刪除。

提取事件

當網路上請求資源時,會觸發提取事件

這提供了在進行網路請求之前查找快取內容的能力。

例如,下面的片段使用快取API檢查請求URL是否已存儲在快取響應中,並在是的情況下返回快取的響應。否則,它執行提取請求並返回它。

1
2
3
4
5
6
7
8
9
10
11
12
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
if (response) { //在快取中找到條目
return response
}
return fetch(event.request)
}
)
)
})

背景同步

背景同步允許推遲發出連接,直到用戶有可工作的網路連接為止。

這是確保用戶可以在離線情況下使用應用程序,並對其進行操作,並將服務器端更新排隊,直到出現連接,而不是顯示無窮旋轉的輪齒嘗試獲取信號。

1
2
3
navigator.serviceWorker.ready.then((swRegistration) => {
return swRegistration.sync.register('event1')
});

此代碼會在Service Worker中偵聽事件:

1
2
3
4
5
self.addEventListener('sync', (event) => {
if (event.tag == 'event1') {
event.waitUntil(doSomething())
}
})

doSomething()返回一個promise。如果失敗,會自動重新排程另一個同步事件和重試,直到成功。

這還可以使應用程序在有可工作連接時立即從服務器更新數據。

推播事件

Service Worker使Web應用可以通過以下方式為用戶提供本機推播通知:

推送和通知實際上是兩個不同的概念和技術,但結合在一起提供了我們所知的推播通知。推送提供了使服務工作者接收來自服務器的信息的機制,而通知則是服務工作者將信息展示給用戶的方式。

由於Service Worker甚至在應用程序未運行時運行,它們可以聽取來的推播事件,並提供用戶通知,或者更新應用程序的狀態。

推送事件是由後端發起的,通過瀏覽器推送服務(例如Firebase提供的服務)。

以下是服務工作者如何聽取傳入推送事件的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
self.addEventListener('push', (event) => {
console.log('Received a push event', event)

const options = {
title: 'I got a message for you!',
body: 'Here is the body of the message',
icon: '/img/icon-192x192.png',
tag: 'tag-for-this-notification',
}

event.waitUntil(
self.registration.showNotification(title, options)
)
})

關於控制台日誌的注釋

如果您的服務工作者中有任何控制台日誌語句(console.log等),請確保開啟Chrome DevTools提供的保留日誌功能或等效功能。

否則,由於服務工作者在頁面加載之前發揮作用,並且控制台在加載頁面之前被清除,您將在控制台中看不到任何日誌。