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

啟用服務工作者,讓瀏覽器知道哪些網頁可在離線狀態下運作

什麼是 Content Indexing API?

使用漸進式網頁應用程式,無論目前網路連線狀態為何,都能讓使用者存取所需資訊 (圖片、影片、文章等)。服務工作者Cache Storage APIIndexedDB 等技術提供的構成元素,可讓您在使用者直接與 PWA 互動時,儲存及提供資料。但打造離線優先的高品質 PWA 只是故事的一部分,如果使用者在離線時不知道網頁應用程式的內容,他們就無法充分利用您導入該項功能的工作。

這是「探索」問題;您的 PWA 如何讓使用者瞭解支援離線的內容,以便探索及瀏覽可用的內容?Content Indexing API 可以解決這個問題。這個解決方案的開發人員專用部分是服務工作程式擴充功能,可讓開發人員將離線可用網頁的網址和中繼資料,新增至瀏覽器維護的本機索引。這項功能強化可在 Chrome 84 以上版本使用。

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

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

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

Content 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() 方法索引網址及其相關中繼資料。將項目新增至索引時,您可以自行選擇。您可能會想根據輸入內容新增至索引,例如按一下「離線儲存」按鈕。或者,您也可以透過定期背景同步等機制,在每次更新快取資料時自動新增項目。

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 提交錯誤。請盡可能提供詳細資訊、重現問題的簡單操作說明,並將「元件」設為 Blink>ContentIndexing

打算使用 API 嗎?

您是否打算在網頁應用程式中使用 Content Indexing API?您的公開支援服務可幫助 Chrome 優先使用各項功能,並讓其他瀏覽器廠商瞭解支援這些功能的重要性。

內容索引功能對安全性和隱私權帶來哪些影響?

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

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