Como criar experiências de pesquisa resilientes com o Workbox

Este codelab mostra como implementar uma experiência de pesquisa resiliente com o Workbox. O app de demonstração usado contém uma caixa de pesquisa que chama um endpoint do servidor e redireciona o usuário para uma página HTML básica.

Medida

Antes de adicionar otimizações, é sempre bom analisar o estado atual do aplicativo.

  • Clique em Remixar para editar para tornar o projeto editável.
  • Para visualizar o site, pressione Ver app. Em seguida, pressione Tela cheia modo tela cheia.

Na nova guia aberta, verifique como o site se comporta ao ficar off-line:

  1. Pressione "Control + Shift + J" (ou "Command + Option + J" no Mac) para abrir o DevTools.
  2. Clique na guia Rede.
  3. Abra o Chrome DevTools e selecione o painel "Network".
  4. Na lista suspensa Limitação, selecione Off-line.
  5. No app de demonstração, digite uma consulta de pesquisa e clique no botão Search.

A página de erro padrão do navegador é mostrada:

Uma captura de tela da UX off-line padrão no navegador.

Fornecer uma resposta substituta

O service worker contém o código para adicionar a página off-line à lista de pré-cache, para que sempre possa ser armazenado em cache no evento install do service worker.

Normalmente, é necessário instruir o Workbox para adicionar esse arquivo à lista de pré-cache no momento da compilação, integrando a biblioteca com a ferramenta de compilação de sua escolha (por exemplo, webpack ou gulp).

Para simplificar, já fizemos isso para você. O código a seguir em public/sw.js faz isso:

const FALLBACK_HTML_URL = '/index_offline.html';
…
workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);

Em seguida, adicione o código para usar a página off-line como uma resposta substituta:

  1. Para ver a fonte, pressione Ver fonte.
  2. Adicione o código abaixo à parte de baixo de public/sw.js:
workbox.routing.setDefaultHandler(new workbox.strategies.NetworkOnly());

workbox.routing.setCatchHandler(({event}) => {
  switch (event.request.destination) {
    case 'document':
      return caches.match(FALLBACK_HTML_URL);
      break;
    default:
      return Response.error();
  }
});

O código realiza as ações a seguir:

  • Define uma estratégia padrão "Apenas na rede" que será aplicada a todas as solicitações.
  • Declara um gerenciador de erro global chamando workbox.routing.setCatchHandler() para gerenciar solicitações com falha. Quando as solicitações são para documentos, uma página HTML off-line substituta é retornada.

Para testar essa funcionalidade:

  1. Volte para a outra guia que está executando seu app.
  2. Defina a lista suspensa Limitação novamente como On-line.
  3. Pressione o botão Voltar do Chrome para voltar à página de pesquisa.
  4. Confira se a caixa de seleção Desativar cache está desativada no DevTools.
  5. Mantenha o botão Atualizar do Chrome pressionado e selecione Esvaziar cache e recarregar o hardware para garantir que o service worker seja atualizado.
  6. Defina a lista suspensa Limitação novamente como Off-line.
  7. Digite uma consulta de pesquisa e clique no botão Pesquisar novamente.

A página HTML substituta é exibida:

Uma captura de tela da UX off-line personalizada no navegador.

Solicitar permissão de notificações

Para simplificar, a página off-line em views/index_offline.html já contém o código para solicitar permissões de notificação em um bloco de script na parte inferior:

function requestNotificationPermission(event) {
  event.preventDefault();

  Notification.requestPermission().then(function (result) {
    showOfflineText(result);
  });
}

O código realiza as ações a seguir:

  • Quando o usuário clica em Assinar notificações, a função requestNotificationPermission() é chamada e chama Notification.requestPermission() para mostrar a solicitação de permissão padrão do navegador. A promessa é resolvida com a permissão escolhida pelo usuário, que pode ser granted, denied ou default.
  • Transmite a permissão resolvida para showOfflineText() para mostrar o texto apropriado ao usuário.

Manter consultas off-line e tentar novamente quando estiver on-line novamente

Em seguida, implemente a Sincronização em segundo plano da caixa de trabalho para manter as consultas off-line, de modo que elas possam ser repetidas quando o navegador detectar que a conectividade retornou.

  1. Abra o arquivo public/sw.js para editar.
  2. Adicione o código abaixo ao final do arquivo:
const bgSyncPlugin = new workbox.backgroundSync.Plugin('offlineQueryQueue', {
  maxRetentionTime: 60,
  onSync: async ({queue}) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        const response = await fetch(entry.request);
        const cache = await caches.open('offline-search-responses');
        const offlineUrl = `${entry.request.url}&notification=true`;
        cache.put(offlineUrl, response);
        showNotification(offlineUrl);
      } catch (error) {
        await this.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

O código realiza as ações a seguir:

  • workbox.backgroundSync.Plugin contém a lógica para adicionar solicitações com falha a uma fila para que possam ser repetidas mais tarde. Essas solicitações serão mantidas no IndexedDB.
  • maxRetentionTime indica por quanto tempo uma solicitação pode ser repetida. Nesse caso, escolhemos 60 minutos (depois dos quais ele será descartado).
  • onSync é a parte mais importante desse código. Esse callback será chamado quando a conexão for restabelecida para que as solicitações na fila sejam recuperadas e, em seguida, buscadas na rede.
  • A resposta da rede é adicionada ao cache do offline-search-responses, anexando o parâmetro de consulta &notification=true, para que essa entrada de cache possa ser capturada quando um usuário clicar na notificação.

Para integrar a sincronização em segundo plano ao seu serviço, defina uma estratégia NetworkOnly para solicitações ao URL de pesquisa (/search_action) e transmita o bgSyncPlugin definido anteriormente. Adicione o código abaixo à parte de baixo de public/sw.js:

const matchSearchUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return url.pathname === '/search_action' && !(notificationParam === 'true');
};

workbox.routing.registerRoute(
  matchSearchUrl,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
);

Isso instrui o Workbox a sempre acessar a rede e, quando as solicitações falharem, usar a lógica de sincronização em segundo plano.

Em seguida, adicione o código a seguir à parte de baixo de public/sw.js para definir uma estratégia de armazenamento em cache para solicitações provenientes de notificações. Use uma estratégia CacheFirst para que eles possam ser disponibilizados a partir do cache.

const matchNotificationUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return (url.pathname === '/search_action' && (notificationParam === 'true'));
};

workbox.routing.registerRoute(matchNotificationUrl,
  new workbox.strategies.CacheFirst({
     cacheName: 'offline-search-responses',
  })
);

Por fim, adicione o código para mostrar notificações:

function showNotification(notificationUrl) {
  if (Notification.permission) {
     self.registration.showNotification('Your search is ready!', {
        body: 'Click to see you search result',
        icon: '/img/workbox.jpg',
        data: {
           url: notificationUrl
        }
     });
  }
}

self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  event.waitUntil(
     clients.openWindow(event.notification.data.url)
  );
});

Testar o recurso

  1. Volte para a outra guia que está executando seu app.
  2. Defina a lista suspensa Limitação novamente como On-line.
  3. Pressione o botão Voltar do Chrome para voltar à página de pesquisa.
  4. Mantenha o botão Atualizar do Chrome pressionado e selecione Esvaziar cache e recarregar o hardware para garantir que o service worker seja atualizado.
  5. Defina a lista suspensa Limitação novamente como Off-line.
  6. Digite uma consulta de pesquisa e clique no botão Pesquisar novamente.
  7. Clique em Inscrever-se para receber notificações.
  8. Quando o Chrome perguntar se você quer conceder permissão ao app para enviar notificações, clique em Permitir.
  9. Digite outra consulta de pesquisa e clique no botão Pesquisar novamente.
  10. Defina a lista suspensa Limitação novamente como On-line.

Quando a conexão for restabelecida, uma notificação será exibida:

Uma captura de tela do fluxo off-line completo.

Conclusão

O Workbox oferece muitos recursos integrados para tornar seus PWAs mais resilientes e envolventes. Neste codelab, você explorou como implementar a API Background Sync usando a abstração Workbox para garantir que as consultas do usuário off-line não sejam perdidas e possam ser repetidas quando a conexão for restabelecida. A demonstração é um app de pesquisa simples, mas você pode usar uma implementação semelhante para cenários e casos de uso mais complexos, incluindo apps de chat, postagem de mensagens em redes sociais etc.