我們在 2015 年推出了背景同步功能,可讓服務工作者將工作延後至使用者有網路連線時再執行。也就是說,使用者可以輸入訊息、按下傳送鍵,然後離開網站,因為系統會在當下或使用者連上網路時傳送訊息。
這項功能相當實用,但需要服務工作者在擷取期間保持運作。對於傳送訊息這類短暫工作而言,這並不是問題,但如果工作耗時過長,瀏覽器就會終止服務工作,否則可能會影響使用者的隱私權和電池。
那麼,如果需要下載的內容可能需要很長的時間,例如電影、Podcast 或遊戲關卡,該怎麼辦呢?這就是背景擷取的用途。
自 Chrome 74 起,背景擷取功能預設為可用。
以下是兩分鐘的快速示範,說明傳統狀態與使用背景擷取的差異:
運作方式
背景擷取的運作方式如下:
- 您可以告訴瀏覽器在背景執行一組擷取作業。
- 瀏覽器會擷取這些內容,並向使用者顯示進度。
- 擷取完成或失敗後,瀏覽器會開啟服務工作站並觸發事件,告知您發生了什麼事。您可以在此決定如何處理回應 (如有)。
如果使用者在步驟 1 後關閉網站網頁,也沒關係,下載作業會繼續進行。由於擷取作業非常明顯且容易中止,因此不會有背景同步作業時間過長的隱私權疑慮。由於服務工作站不會持續執行,因此不會有濫用系統的疑慮,例如在背景挖掘比特幣。
在某些平台 (例如 Android) 上,瀏覽器可能會在步驟 1 後關閉,因為瀏覽器可將擷取作業交給作業系統。
如果使用者在離線時啟動下載作業,或在下載期間離線,背景擷取作業就會暫停,並在稍後恢復。
API
功能偵測
如同任何新功能,您需要偵測瀏覽器是否支援這項功能。如要使用背景擷取功能,只要執行以下操作即可:
if ('BackgroundFetchManager' in self) {
// This browser supports Background Fetch!
}
開始背景擷取
主要 API 會掛接 service worker 註冊,因此請務必先註冊 service worker。然後執行下列步驟:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch('my-fetch', ['/ep-5.mp3', 'ep-5-artwork.jpg'], {
title: 'Episode 5: Interesting things.',
icons: [{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
}],
downloadTotal: 60 * 1024 * 1024,
});
});
backgroundFetch.fetch
會採用三個引數:
參數 | |
---|---|
id |
string 可明確識別此背景擷取作業。 如果 ID 與現有的背景擷取相符, |
requests |
Array<Request|string>
要擷取的內容。字串會視為網址,並透過 new Request(theString) 轉換為 Request 。只要資源允許透過 CORS 擷取,您就可以從其他來源擷取資料。 注意:Chrome 目前不支援需要 CORS 預先飛行檢查的請求。 |
options |
物件可能包含下列項目: |
options.title |
string 瀏覽器會在進度顯示時一併顯示的標題。 |
options.icons |
Array<IconDefinition> 包含 `src`、`size` 和 `type` 的物件陣列。 |
options.downloadTotal |
number 回應主體的總大小 (解壓縮後)。 雖然這是選用項目,但我們強烈建議您提供。用於告知使用者下載檔案的大小,並提供進度資訊。如果您未提供這項資訊,瀏覽器會告知使用者大小不明,因此使用者可能會更有可能中止下載作業。 如果背景擷取下載次數超過此處指定的數量,系統就會中止下載。如果下載次數小於 |
backgroundFetch.fetch
會傳回一個承諾,並透過 BackgroundFetchRegistration
解析。我稍後會詳細說明。如果使用者已選擇不下載,或提供的其中一個參數無效,則承諾會遭到拒絕。
為單一背景擷取作業提供多個要求,可讓您將對使用者而言在邏輯上是單一事物的內容合併。舉例來說,一部電影可能會分割成數千個資源 (通常是 MPEG-DASH),並附帶圖片等額外資源。遊戲的一個關卡可能會分散在許多 JavaScript、圖片和音訊資源中。但對使用者來說,這只是「電影」或「等級」。
取得現有的背景擷取
您可以透過以下方式取得現有的背景擷取作業:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});
…傳遞所需背景擷取作業的 id。如果沒有任何具有該 ID 的背景擷取作業,get
會傳回 undefined
。
背景擷取作業從註冊時開始,直到成功、失敗或中止為止,都會視為「有效」。
您可以使用 getIds
取得所有有效背景擷取作業的清單:
navigator.serviceWorker.ready.then(async (swReg) => {
const ids = await swReg.backgroundFetch.getIds();
});
背景擷取註冊
BackgroundFetchRegistration
(上述範例中的 bgFetch
) 具備下列屬性:
屬性 | |
---|---|
id |
string 背景擷取作業的 ID。 |
uploadTotal |
number 要傳送至伺服器的位元組數。 |
uploaded |
number 成功傳送的位元組數。 |
downloadTotal |
number 註冊背景動態擷取時提供的值,或零。 |
downloaded |
number 成功接收的位元組數。 這個值可能會降低。舉例來說,如果連線中斷且無法繼續下載,瀏覽器會從頭開始重新擷取該資源。 |
result |
可以是下列其中一項:
|
failureReason |
可以是下列其中一項:
|
recordsAvailable |
boolean 是否可以存取基礎要求/回應? 一旦設為 False,就無法使用 |
方法 | |
abort() |
傳回 Promise<boolean> 終止背景擷取作業。 如果擷取作業已成功中止,則傳回的承諾會以 true 解析。 |
matchAll(request, opts) |
傳回 Promise<Array<BackgroundFetchRecord>> 取得要求和回應。 這裡的引數與快取 API 相同。不帶引數的呼叫會傳回所有記錄的承諾。 詳情請見下面的說明。 |
match(request, opts) |
傳回 Promise<BackgroundFetchRecord> 。與上述相同,但會以第一個相符項目解析。 |
活動 | |
progress |
當 uploaded 、downloaded 、result 或 failureReason 有任何變更時觸發。 |
追蹤進度
這項操作可透過 progress
事件完成。請注意,downloadTotal
是您提供的任何值,如果未提供值,則為 0
。
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(bgFetch.downloaded / bgFetch.downloadTotal * 100);
console.log(`Download progress: ${percent}%`);
});
取得要求和回應
bgFetch.match('/ep-5.mp3').then(async (record) => {
if (!record) {
console.log('No record found');
return;
}
console.log(`Here's the request`, record.request);
const response = await record.responseReady;
console.log(`And here's the response`, response);
});
record
是 BackgroundFetchRecord
,如下所示:
屬性 | |
---|---|
request |
Request 提供的要求。 |
responseReady |
Promise<Response> 擷取的回應。 回應可能尚未收到,因此會在承諾後方顯示。如果擷取失敗,承諾就會遭到拒絕。 |
Service worker 事件
活動 | |
---|---|
backgroundfetchsuccess |
已成功擷取所有內容。 |
backgroundfetchfailure |
發生一或多個擷取失敗。 |
backgroundfetchabort |
發生一或多個擷取失敗。
只有在您想清理相關資料時,這項功能才會真正派上用場。 |
backgroundfetchclick |
使用者點選下載進度 UI。 |
事件物件包含下列項目:
屬性 | |
---|---|
registration |
BackgroundFetchRegistration |
方法 | |
updateUI({ title, icons }) |
可讓您變更最初設定的標題/圖示。這是選填項目,但可讓您在必要時提供更多背景資訊。您只能在 backgroundfetchsuccess 和 backgroundfetchfailure 事件中執行這項操作 *一次*。 |
回應成功/失敗
我們已經看到 progress
事件,但這只有在使用者開啟網站的網頁時才有用。背景擷取的主要優點是,即使使用者離開網頁,甚至關閉瀏覽器,這些內容仍會繼續運作。
如果背景擷取作業順利完成,服務工作者會收到 backgroundfetchsuccess
事件,而 event.registration
則會是背景擷取註冊。
發生此事件後,您就無法再存取擷取的請求和回應,因此如果想保留這些項目,請將其移至 快取 API 等位置。
如同大多數服務工作程事件,請使用 event.waitUntil
,讓服務工作程知道事件何時完成。
例如,在服務工作者中:
addEventListener('backgroundfetchsuccess', (event) => {
const bgFetch = event.registration;
event.waitUntil(async function() {
// Create/open a cache.
const cache = await caches.open('downloads');
// Get all the records.
const records = await bgFetch.matchAll();
// Copy each request/response across.
const promises = records.map(async (record) => {
const response = await record.responseReady;
await cache.put(record.request, response);
});
// Wait for the copying to complete.
await Promise.all(promises);
// Update the progress notification.
event.updateUI({ title: 'Episode 5 ready to listen!' });
}());
});
失敗可能歸因於單一 404 錯誤,而這類錯誤對您來說可能不重要,因此還是建議您將部分回應複製到快取中,如上所述。
回應點擊
顯示下載進度和結果的 UI 可供點選。服務工作者中的 backgroundfetchclick
事件可讓您對此做出回應。如上所述,event.registration
將會是背景擷取註冊。
這個事件的常見操作是開啟視窗:
addEventListener('backgroundfetchclick', (event) => {
const bgFetch = event.registration;
if (bgFetch.result === 'success') {
clients.openWindow('/latest-podcasts');
} else {
clients.openWindow('/download-progress');
}
});
其他資源
更正:這篇文章的舊版誤將背景擷取功能稱為「網路標準」。這個 API 目前不在標準程序中,您可以在 WICG 中找到規格,做為社群群組報告草稿。