我們在 2015 年推出背景同步處理,可讓服務工作站延遲工作,直到使用者連上網路為止。這表示使用者可以輸入訊息、按下傳送按鈕,並讓網站知道訊息會在現在或能夠連線時傳送。
這是一項實用功能,但它需要服務工作站在擷取期間保持運作。以傳送訊息等小作業來說,這並非問題,但如果工作執行時間過長,瀏覽器就會終止服務工作站,否則對使用者的隱私和電池有風險。
因此,如果需要下載可能需要較長時間的內容,例如電影、Podcast 或遊戲的關卡,該怎麼做呢?這就是「背景擷取」的功能。
自 Chrome 74 版起,系統會預設使用背景擷取功能。
以下提供 2 分鐘的簡短示範,比較傳統的內容和背景擷取功能:
運作方式
背景擷取的運作方式如下:
- 您可以指示瀏覽器在背景執行一組擷取作業。
- 瀏覽器會擷取這些內容,並向使用者顯示進度。
- 擷取完成或失敗後,瀏覽器會開啟 Service Worker 並觸發事件,讓您瞭解情況。您可以在這裡決定回應 (如有) 的處理方式。
如果使用者在步驟 1 後關閉了網站頁面,沒有問題,下載作業會繼續執行。由於擷取作業的瀏覽權限相當高,而且很容易取消,因此採用長效做法的背景同步處理工作並不構成隱私權方面的疑慮。由於 Service Worker 不是持續執行,因此您不必擔心可能會濫用系統,例如在背景中採礦比特幣。
在部分平台 (例如 Android) 中,瀏覽器可在步驟 1 之後關閉,因為瀏覽器可以將擷取作業交給作業系統。
如果使用者在離線時開始下載,或在下載期間離線,背景擷取就會暫停,並於稍後繼續。
API
特徵偵測
與任何新功能一樣,建議您偵測瀏覽器是否支援此功能。背景擷取作業非常簡單:
if ('BackgroundFetchManager' in self) {
// This browser supports Background Fetch!
}
開始背景擷取
主要 API 會停止服務工作站註冊,因此請確認您已註冊 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 回應主體的總大小 (未經 gzip 壓縮後)。 此為選填欄位,但我們極力建議您提供。此屬性用於告知使用者下載大小,並提供進度資訊。如果您並未提供這項資訊,瀏覽器就會向使用者表明尺寸不明,因此使用者可能會取消下載。 如果背景擷取下載次數超過這裡提供的數量,系統將取消作業。如果下載內容小於 |
backgroundFetch.fetch
會傳回承諾使用 BackgroundFetchRegistration
解析。稍後會再詳細說明如果使用者已選擇停止下載,或提供的其中一個參數無效,Remise 就會拒絕。
如果為單一背景擷取提供多個要求,您就可以將根據邏輯,為使用者合併的單一項目。例如,電影可分割成 1000 年代的資源 (與 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> 取消背景擷取作業。 如果擷取作業成功取消,傳回的 promise 會解析為 true。 |
matchAll(request, opts) |
傳回 Promise<Array<BackgroundFetchRecord>> 取得要求和回應。 這裡的引數與快取 API 相同。呼叫不使用引數時則會傳回所有記錄的 promise。 請見下方的詳細說明。 |
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> 擷取的回應。 由於尚未收到回覆,因此回應才會解鎖。如果擷取失敗,promise 就會拒絕。 |
Service Worker 事件
活動 | |
---|---|
backgroundfetchsuccess |
已成功擷取所有項目。 |
backgroundfetchfailure |
一或多個擷取作業失敗。 |
backgroundfetchabort |
一或多項擷取作業失敗。
只有在您想要清理相關資料時,才能使用這項功能。 |
backgroundfetchclick |
使用者點選下載進度使用者介面。 |
事件物件包含下列項目:
屬性 | |
---|---|
registration |
BackgroundFetchRegistration |
方法 | |
updateUI({ title, icons }) |
可讓您變更一開始設定的標題/圖示。這是選填欄位,但可讓您視需要提供更多背景資訊。在 backgroundfetchsuccess 和 backgroundfetchfailure 事件期間,您只能 *執行一次* 這項操作。 |
回應成功/失敗
已看過 progress
事件,但這只有在使用者開啟網站頁面時才實用。背景擷取的主要優點是,在使用者離開頁面,甚至是關閉瀏覽器後仍會繼續運作。
如果背景擷取成功完成,服務工作站就會收到 backgroundfetchsuccess
事件,而 event.registration
會是背景擷取註冊作業。
在此事件過後,您就無法再存取擷取的要求和回應,因此如要保留這些要求,請將這些內容移至快取 API 之類的位置。
和大部分的 Service Worker 事件一樣,請使用 event.waitUntil
,讓 Service Worker 知道事件完成時間。
例如,在您的服務工作站中:
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 可能對您來說並不重要,因此仍值得將一些回應複製到快取中,如上所述。
回應點擊
顯示下載進度和結果的使用者介面可點選。Service Worker 中的 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 中找到「草擬社群群組報告」中的規格。