Como indexar suas páginas compatíveis com o modo off-line com a API Content Indexing

Ativar os workers de serviço para informar aos navegadores quais páginas funcionam off-line

O que é a API Content Indexing?

Usar um app da Web progressivo significa ter acesso a informações importantes para as pessoas, como imagens, vídeos, artigos e muito mais, independentemente do estado atual da sua conexão de rede. Tecnologias como service workers, a API Cache Storage e o IndexedDB fornecem os blocos de construção para armazenar e fornecer dados quando as pessoas interagem diretamente com um PWA. No entanto, criar um PWA de alta qualidade e que prioriza o modo off-line é apenas parte da história. Se as pessoas não perceberem que o conteúdo de um app da Web está disponível enquanto elas estão off-line, elas não vão aproveitar ao máximo o trabalho que você fez para implementar essa funcionalidade.

Esse é um problema de descoberta. Como sua PWA pode informar aos usuários sobre o conteúdo off-line para que eles possam descobrir e conferir o que está disponível? A API Content Indexing é uma solução para esse problema. A parte voltada ao desenvolvedor dessa solução é uma extensão para service workers, que permite aos desenvolvedores adicionar URLs e metadados de páginas com capacidade off-line a um índice local mantido pelo navegador. Essa melhoria está disponível no Chrome 84 e em versões mais recentes.

Quando o índice for preenchido com conteúdo do seu PWA e de outros PWAs instalados, ele será exibido pelo navegador, conforme mostrado abaixo.

Captura de tela do item de menu "Downloads" na página "Nova guia" do Chrome.
Primeiro, selecione o item de menu Downloads na página Nova guia do Chrome.
Mídia e artigos adicionados ao índice.
A mídia e os artigos adicionados ao índice vão aparecer na seção Artigos para você.

Além disso, o Chrome pode recomendar conteúdo de forma proativa quando detecta que um usuário está off-line.

A API Content Indexing não é uma maneira alternativa de armazenar conteúdo em cache. É uma maneira de fornecer metadados sobre páginas que já estão armazenadas em cache pelo worker do serviço para que o navegador possa mostrar essas páginas quando as pessoas provavelmente quiserem acessar. A API Content Indexing ajuda na descoberta de páginas em cache.

Confira na prática

A melhor maneira de conhecer a API Content Indexing é testar um aplicativo de exemplo.

  1. Verifique se você está usando um navegador e uma plataforma compatíveis. No momento, essa opção está limitada ao Chrome 84 ou mais recente no Android. Acesse about://version para saber qual versão do Chrome você está usando.
  2. Acesse https://contentindex.dev.
  3. Clique no botão + ao lado de um ou mais itens da lista.
  4. (Opcional) Desative o Wi-Fi e a conexão de dados móveis do dispositivo ou ative o modo avião para simular a desconexão do navegador.
  5. Escolha Downloads no menu do Chrome e mude para a guia Artigos para você.
  6. Navegue pelo conteúdo que você salvou anteriormente.

Confira a origem do aplicativo de exemplo no GitHub.

Outro aplicativo de exemplo, um Scrapbook PWA, ilustra o uso da API Content Indexing com a API Web Share Target. O código demonstra uma técnica para manter a API Content Indexing sincronizada com os itens armazenados por um app da Web usando a API Cache Storage.

Como usar a API

Para usar a API, seu app precisa ter um worker de serviço e URLs que podem ser navegados off-line. Se o app da Web não tiver um service worker, as bibliotecas do Workbox podem simplificar a criação dele.

Que tipo de URLs podem ser indexados como compatíveis com o modo off-line?

A API oferece suporte à indexação de URLs correspondentes a documentos HTML. Por exemplo, um URL de um arquivo de mídia em cache não pode ser indexado diretamente. Em vez disso, você precisa fornecer um URL para uma página que exiba mídia e funcione off-line.

Um padrão recomendado é criar uma página HTML "visualizador" que possa aceitar o URL da mídia como um parâmetro de consulta e mostrar o conteúdo do arquivo, possivelmente com controles ou conteúdo adicionais na página.

Os apps da Web só podem adicionar URLs ao índice de conteúdo que estão no escopo do worker de serviço atual. Em outras palavras, um app da Web não poderia adicionar um URL pertencente a um domínio completamente diferente ao índice de conteúdo.

Visão geral

A API Content Indexing oferece suporte a três operações: adicionar, listar e remover metadados. Esses métodos são expostos por uma nova propriedade, index, que foi adicionada à interface ServiceWorkerRegistration.

A primeira etapa para indexar conteúdo é receber uma referência ao ServiceWorkerRegistration atual. Usar navigator.serviceWorker.ready é a maneira mais simples:

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

Se você estiver fazendo chamadas para a API Content Indexing em um service worker, em vez de dentro de uma página da Web, poderá se referir ao ServiceWorkerRegistration diretamente pelo registration. Ele já será definido como parte do ServiceWorkerGlobalScope..

Como adicionar à indexação

Use o método add() para indexar URLs e os metadados associados. Você escolhe quando os itens são adicionados ao índice. Talvez você queira adicionar ao índice em resposta a uma entrada, como clicar no botão "salvar off-line". Ou você pode adicionar itens automaticamente sempre que os dados armazenados em cache forem atualizados por um mecanismo como a sincronização em segundo plano periódica.

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',
});

A adição de uma entrada afeta apenas o índice de conteúdo. Ela não adiciona nada ao cache.

Caso extremo: chame add() do contexto window se os ícones dependerem de um gerenciador fetch

Quando você chama add(), o Chrome faz uma solicitação para o URL de cada ícone para garantir que ele tenha uma cópia do ícone a ser usada ao exibir uma lista de conteúdo indexado.

  • Se você chamar add() do contexto window (ou seja, da sua página da Web), essa solicitação vai acionar um evento fetch no service worker.

  • Se você chamar add() no service worker (talvez dentro de outro manipulador de eventos), a solicitação não vai acionar o manipulador fetch do service worker. Os ícones serão buscados diretamente, sem a participação de nenhum service worker. Considere isso se os ícones dependerem do gerenciador fetch, talvez porque eles existam apenas no cache local e não na rede. Se for o caso, chame apenas add() do contexto window.

Como listar o conteúdo do índice

O método getAll() retorna uma promessa para uma lista iterável de entradas indexadas e os metadados delas. As entradas retornadas vão conter todos os dados salvos com add().

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

Como remover itens do índice

Para remover um item do índice, chame delete() com o id do item a ser removido:

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

Chamar delete() afeta apenas o índice. Ele não exclui nada do cache.

Como processar um evento de exclusão de usuário

Quando o navegador mostra o conteúdo indexado, ele pode incluir a própria interface de usuário com um item de menu Excluir, às pessoas a chance de indicar que elas terminaram de visualizar o conteúdo indexado anteriormente. Confira como a interface de exclusão parece no Chrome 80:

O item de menu "Excluir".

Quando alguém seleciona esse item de menu, o worker de serviço do app da Web recebe um evento contentdelete. Embora o processamento desse evento seja opcional, ele oferece uma chance para que o worker de serviço "limpe" o conteúdo, como arquivos de mídia em cache local, que alguém indicou que não precisa mais.

Não é necessário chamar registration.index.delete() no gerenciador contentdelete. Se o evento foi acionado, a exclusão de índice relevante já foi realizada pelo navegador.

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.
});

Feedback sobre o design da API

Há algo na API que é estranho ou não funciona como esperado? Ou há partes que faltam para implementar sua ideia?

Registre um problema no repositório do GitHub sobre a API Content Indexing ou adicione sua opinião a um problema existente.

Problemas com a implementação?

Você encontrou um bug na implementação do Chrome?

Registre um bug em https://new.crbug.com. Inclua o máximo de detalhes possível, instruções simples para reprodução e defina Components como Blink>ContentIndexing.

Planeja usar a API?

Você planeja usar a API Content Indexing no seu app da Web? Seu apoio público ajuda o Chrome a priorizar recursos e mostra a outros fornecedores de navegadores a importância de oferecer suporte a eles.

Quais são algumas implicações de segurança e privacidade da indexação de conteúdo?

Confira as respostas fornecidas em resposta ao questionário de segurança e privacidade do W3C. Se você tiver outras dúvidas, inicie uma discussão no repositório do GitHub do projeto.

Imagem principal de Maksym Kaharlytskyi no Unsplash.