使用 Content Indexing API 為可離線存取的網頁建立索引

讓服務工作人員判斷哪些網頁可離線運作

使用「漸進式網頁應用程式」,意味著無論網路連線的目前狀態為何,都能取得人們重視的資訊,例如圖片、影片、文章等內容。服務工作站Cache Storage APIIndexedDB 等技術提供了這些基礎功能,可在使用者直接與 PWA 互動時,儲存及提供資料。不過,建構高品質的離線優先 PWA 只是故事的開端。如果使用者不知道網頁應用程式在離線時仍可使用,就無法充分利用您在實作這項功能時所投入的努力。

這是探索問題;您的 PWA 如何讓使用者知道其可離線使用的內容,讓他們可以探索及查看可用的內容?內容索引 API 就是解決這個問題的解決方案。本解決方案中開發人員的部分是對服務工作處理程序的擴充功能,可讓開發人員將網址和中繼資料新增至瀏覽器維護的本機索引。這項功能強化可在 Chrome 84 以上版本使用。

從 PWA 以及任何其他已安裝的 PWA 填入內容後,瀏覽器就會顯示這個索引,如下所示。

Chrome 新分頁頁面上的「下載」選單項目螢幕截圖。
首先,請在 Chrome 的新分頁頁面上選取「下載」選單項目。
已新增至索引的媒體和文章。
已加入索引的媒體和文章會顯示在「專屬文章」專區。

此外,Chrome 還能偵測使用者是否離線,並主動推薦內容。

Content Indexing API 不是另一種快取內容的方式。這是一種提供服務 worker 已快取的網頁中繼資料的方法,讓瀏覽器在使用者可能想查看這些網頁時,顯示這些網頁。Content Indexing API 有助於探索快取網頁。

實例觀摩

如要瞭解 Content Indexing API,最好的方式就是試用範例應用程式。

  1. 請確認你使用的是支援的瀏覽器和平台。目前僅限於 Android 上的 Chrome 84 以上版本。前往 about://version 查看你目前使用的 Chrome 版本。
  2. 前往 https://contentindex.dev
  3. 在清單中按一下一或多個項目旁邊的 + 按鈕。
  4. (選用) 停用裝置的 Wi-Fi 和行動數據連線,或啟用飛航模式,模擬瀏覽器離線的情況。
  5. 從 Chrome 選單中選擇「下載內容」,然後切換至「為你推薦的文章」分頁。
  6. 瀏覽您先前儲存的內容。

您可以在 GitHub 上查看應用程式範例的原始碼

另一個範例應用程式 Scrapbook PWA 則說明如何搭配使用 Content Indexing API 和 Web Share Target API。以下程式碼展示了一種技巧,可讓 Content Indexing API 與使用 Cache Storage API 的網頁應用程式儲存的項目保持同步。

使用 API

如要使用 API,您的應用程式必須具備服務工作者和可在離線狀態下瀏覽的網址。如果您的網路應用程式目前沒有服務工作者,Workbox 程式庫可簡化建立服務工作者的程序。

哪些類型的網址可建立為離線可用的索引?

這個 API 支援索引與 HTML 文件相對應的網址。例如,您無法直接為快取媒體檔案的網址建立索引。您必須提供顯示媒體的網頁網址,且該網頁必須可離線運作。

建議的模式是建立「檢視器」HTML 頁面,可接受基礎媒體網址做為查詢參數,然後顯示檔案的內容,頁面上可能還有其他控制項或內容。

網頁應用程式只能將網址新增至目前服務工作者的範圍內的內容索引。換句話說,網頁應用程式無法在內容索引中加入屬於完全不同網域的網址。

總覽

Content Indexing API 支援三項作業:新增、列出和移除中繼資料。這些方法來自新增至 ServiceWorkerRegistration 介面的新屬性 index

為內容建立索引的第一步,就是取得目前 ServiceWorkerRegistration 的參照。使用 navigator.serviceWorker.ready 是最直接的方法:

const registration = await navigator.serviceWorker.ready;

// Remember to feature-detect before using the API:
if ('index' in registration) {
 
// Your Content Indexing API code goes here!
}

如果您是從服務工作者 (而非網頁) 呼叫內容索引 API,可以直接透過 registration 參照 ServiceWorkerRegistration已定義ServiceWorkerGlobalScope. 的一部分

新增至索引

使用 add() 方法索引網址及其相關中繼資料。您可以選擇何時將項目新增至索引。建議您加入索引來回應輸入內容,例如按一下「Save 離線」按鈕。或者,您也可以透過定期背景同步等機制,在每次更新快取資料時自動新增項目。

await registration.index.add({
 
// Required; set to something unique within your web app.
  id
: 'article-123',

 
// Required; url needs to be an offline-capable HTML page.
  url
: '/articles/123',

 
// Required; used in user-visible lists of content.
  title
: 'Article title',

 
// Required; used in user-visible lists of content.
  description
: 'Amazing article about things!',

 
// Required; used in user-visible lists of content.
  icons
: [{
    src
: '/img/article-123.png',
    sizes
: '64x64',
    type
: 'image/png',
 
}],

 
// Optional; valid categories are currently:
 
// 'homepage', 'article', 'video', 'audio', or '' (default).
  category
: 'article',
});

新增項目只會影響內容索引,不會在快取中新增任何內容。

極端案例:如果圖示依賴 fetch 處理常式,請從 window 內容中呼叫 add()

當您呼叫 add() 時,Chrome 會要求取得每個圖示的網址,確保在顯示已編入索引的內容清單時,可以使用圖示的副本。

  • 如果您從 window 內容 (亦即從網頁) 呼叫 add(),這項要求會在 Service Worker 上觸發 fetch 事件。

  • 如果您在服務工作者 (可能在其他事件處理常式中) 內呼叫 add(),要求就「不會」觸發服務工作者的 fetch 處理常式。圖示將直接擷取,服務工作人員不需進行任何操作。如果您的圖示依賴 fetch 處理常式,可能是因為圖示只存在於本機快取中,而非網路中,因此請記住這一點。如果這樣做,請務必只從 window 結構定義呼叫 add()

列出索引的內容

getAll() 方法會針對可迭代的索引項目清單和其中繼資料,傳回承諾。傳回的項目會包含使用 add() 儲存的所有資料。

const entries = await registration.index.getAll();
for (const entry of entries) {
 
// entry.id, entry.launchUrl, etc. are all exposed.
}

從索引中移除項目

如要從索引中移除項目,請呼叫 delete() 並附上要移除的項目的 id

await registration.index.delete('article-123');

呼叫 delete() 只會影響索引。不會從快取中刪除任何內容。

處理使用者刪除事件

瀏覽器顯示已編入索引的內容時,可能會提供專屬使用者介面,並附上「Delete」選單項目,讓使用者可在瀏覽完先前已編入索引的內容後,選擇刪除該內容。以下是 Chrome 80 中刪除介面的樣貌:

刪除選單項目。

當使用者選取該選單項目時,您的網頁應用程式服務工作者就會收到 contentdelete 事件。雖然處理這個事件並非必要,但這可讓服務工作者有機會「清理」使用者已表示完成的內容,例如已快取至本機的媒體檔案。

您不需要在 contentdelete 處理常式中呼叫 registration.index.delete();如果事件已觸發,瀏覽器就會執行相關索引刪除作業。

self.addEventListener('contentdelete', (event) => {
 
// event.id will correspond to the id value used
 
// when the indexed content was added.
 
// Use that value to determine what content, if any,
 
// to delete from wherever your app stores it—usually
 
// the Cache Storage API or perhaps IndexedDB.
});

對 API 設計提供意見

API 是否有任何不便之處,或無法正常運作?或者,您是否缺少實現想法所需的部分?

請在 Content Indexing API 說明 GitHub 存放區中提出問題,或是在現有問題中加入您的想法。

導入時發生問題?

你是否發現 Chrome 實作項目有錯誤?

請前往 https://new.crbug.com 提交錯誤。請盡可能提供詳細資訊、重現問題的簡單操作說明,並將「元件」設為 Blink>ContentIndexing

打算使用 API 嗎?

您是否打算在網頁應用程式中使用 Content Indexing API?您的公開支援資訊有助於 Chrome 優先處理功能,並向其他瀏覽器供應商顯示支援這些功能的重要性。

內容索引對安全性和隱私權有何影響?

請參閱 W3C 安全性和隱私權問卷調查答案。如有其他問題,請透過專案的 GitHub 存放區開始討論。

主頁橫幅圖片由 Maksym Kaharlytskyi 提供,取自 Unsplash