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

讓服務工作處理程序知道哪些網頁可離線運作

什麼是 Content Indexing API?

使用漸進式網頁應用程式,表示無論目前的網路連線狀態為何,您都能取得使用者關注的資訊,例如圖片、影片和文章等等。服務工作站Cache Storage APIIndexedDB 等技術能運用多種基礎元素,協助讀者直接與 PWA 互動,儲存及提供資料。但建構優質的離線優先 PWA 只是其中一環。假如使用者不知道網頁應用程式可以在離線狀態下存取內容,就無法充分利用您在實作此功能時所投入的工作。

這屬於「探索」問題:您的 PWA 如何讓使用者瞭解具有離線功能的內容,以便他們發掘及查看可用內容?Content Indexing API 可以解決這個問題。此解決方案中開發人員面臨的部分是服務工作站的擴充功能,可讓開發人員將支援離線網頁的網址和中繼資料,加入瀏覽器維護的本機索引。這項強化功能適用於 Chrome 84 以上版本。

將 PWA 的內容以及已安裝的 PWA 填入索引後,瀏覽器就會顯示該索引,如下所示。

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

此外,Chrome 偵測到使用者處於離線狀態時,可以主動推薦內容。

Content Indexing API 不是快取內容的替代方法。這種方法可以針對服務工作站已快取的網頁提供中繼資料,如此一來,如果使用者想要檢視網頁,瀏覽器就能夠顯示這些網頁。Content Indexing API 可協助系統探索快取網頁,

實例觀摩

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

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

您可以前往 GitHub 查看範例應用程式的來源

另一個範例應用程式是 Scrapbook PWA,此應用程式說明如何搭配 Web Share Target API 使用 Content Indexing API。程式碼示範如何使用 Cache Storage API,讓 Content Indexing API 與網頁應用程式儲存的項目保持同步。

使用 API

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

哪些類型的網址可以建立索引並支援離線功能?

這個 API 支援為 HTML 文件對應的網址建立索引。舉例來說,無法直接建立索引的快取媒體檔案網址。而是需要提供顯示媒體且可離線運作的頁面網址。

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

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

總覽

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!
}

如果您是在服務工作站內 (而非網頁) 呼叫 Content Indexing API,可以直接透過 registration 參照 ServiceWorkerRegistration。這個項目已定義ServiceWorkerGlobalScope. 的一部分

新增至索引

請使用 add() 方法為網址及其相關聯的中繼資料建立索引。將項目新增至索引時,或要自行選擇。您可能會依據輸入內容新增索引,例如點選「離線儲存」按鈕。或者,您也可以在每次快取資料透過定期背景同步處理等機制更新時,自動新增項目。

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() (也就是從網頁呼叫),這項要求會在服務工作站上觸發 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.
}

從索引中移除項目

如要從索引中移除項目,請使用要移除的項目的 id 呼叫 delete()

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 回報錯誤。請盡可能提供詳細資料、簡易的重現操作說明,並將 Components 設為 Blink>ContentIndexing

打算使用 API 嗎?

打算在網頁應用程式中使用 Content Indexing API 嗎?您的公開支援可協助 Chrome 決定各項功能的優先順序,以及向其他瀏覽器廠商瞭解這項功能有多重要。

內容索引的安全性和隱私權會對安全性和隱私權造成哪些影響?

查看針對 W3C 的安全性與隱私權問卷取得的答案。如有其他問題,請透過專案的 GitHub 存放區發起討論。

主頁橫幅由 Maksym Kaharlytskyi 撰寫,此為 Unsplash 網站上。