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 tem uma caixa de pesquisa que chama um endpoint do servidor e redireciona o usuário para uma página HTML básica.

Medir

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

  • Clique em Remix to Edit para tornar o projeto editável.
  • Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen tela cheia.

Na nova guia que acabou de abrir, verifique como o site se comporta quando fica off-line:

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

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 worker de serviço contém o código para adicionar a página off-line à lista de pré-cache, para que ela possa ser armazenada em cache no evento install do worker de serviço.

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

Para simplificar, já fizemos isso por você. O código abaixo 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 alternativa:

  1. Para conferir a origem, pressione View Source.
  2. Adicione o seguinte código à 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 somente de rede padrão que será aplicada a todas as solicitações.
  • Declara um gerenciador de erros 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 alternativa é retornada.

Para testar essa funcionalidade:

  1. Volte para a outra guia em que o app está sendo executado.
  2. Defina a lista suspensa Limitação como Online.
  3. Pressione o botão Voltar do Chrome para voltar à página de pesquisa.
  4. Verifique se a caixa de seleção Desativar cache no DevTools está desativada.
  5. Toque e mantenha pressionado o botão Reload do Chrome e selecione Empty cache and hard reload para garantir que o service worker seja atualizado.
  6. Defina a lista suspensa Limitação como Off-line novamente.
  7. Digite uma consulta e clique no botão Pesquisar novamente.

A página HTML de substituição é mostrada:

Captura de tela da UX personalizada off-line 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 de baixo:

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, o que 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 voltar a ficar on-line

Em seguida, implemente a sincronização em segundo plano do Workbox para manter as consultas off-line, para que elas possam ser repetidas quando o navegador detectar que a conectividade foi restabelecida.

  1. Abra public/sw.js para edição.
  2. Adicione o seguinte código 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 elas possam ser tentadas novamente mais tarde. Essas solicitações serão mantidas na IndexedDB.
  • maxRetentionTime indica o tempo que uma solicitação pode ser repetida. Neste caso, escolhemos 60 minutos (após esse período, o cookie será descartado).
  • onSync é a parte mais importante deste código. Esse callback será chamado quando a conexão for restabelecida para que as solicitações enfileiradas sejam recuperadas e extraídas da rede.
  • A resposta da rede é adicionada ao cache 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 seguinte código à 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 informa ao Workbox que ele sempre vai para a rede e, quando as solicitações falharem, use a lógica de sincronização em segundo plano.

Em seguida, adicione o código abaixo à 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 exibidos no 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 as 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 em que o app está sendo executado.
  2. Defina a lista suspensa Limitação como Online.
  3. Pressione o botão Voltar do Chrome para voltar à página de pesquisa.
  4. Toque e mantenha pressionado o botão Reload do Chrome e selecione Empty cache and hard reload para garantir que o service worker seja atualizado.
  5. Defina a lista suspensa Limitação como Off-line novamente.
  6. Digite uma consulta 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 e clique no botão Pesquisar novamente.
  10. Defina a lista suspensa Throttling como Online novamente.

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

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ê aprendeu a implementar a API Background Sync usando a abstração do 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 uma rede social etc.