Como processar solicitações de navegação

Responda a solicitações de navegação sem esperar na rede usando um service worker.

As solicitações de navegação são solicitações de documentos HTML feitas pelo navegador sempre que você insere um novo URL na barra de navegação ou segue um link em uma página que leva a um novo URL. É aqui que os service workers causam o maior impacto no desempenho: se você usar um service worker para responder a solicitações de navegação sem esperar pela rede, poderá garantir que as navegações sejam rápidas de maneira confiável, além de serem resilientes quando a rede estiver indisponível. Essa é a maior vantagem de desempenho que vem de um service worker, em comparação com o que é possível com armazenamento em cache HTTP.

Conforme detalhado no guia Identificar recursos carregados da rede, uma solicitação de navegação é a primeira de possivelmente muitas solicitações feitas na "cascata" do tráfego de rede. O HTML carregado por uma solicitação de navegação inicia o fluxo de todas as outras solicitações de sub-recursos, como imagens, scripts e estilos.

No manipulador de eventos fetch de um service worker, você pode determinar se uma solicitação é uma navegação verificando a propriedade request.mode no FetchEvent. Se ele estiver definido como 'navigate', será uma solicitação de navegação.

Como regra geral, não use Cache-Control headers de longa duração para armazenar em cache a resposta HTML para uma solicitação de navegação. Normalmente, elas são atendidas pela rede, com Cache-Control: no-cache, para garantir que o HTML e a cadeia de solicitações de rede subsequentes estejam (razoavelmente) atualizados. Infelizmente, acessar a rede sempre que o usuário navega para uma nova página significa que a navegação pode ser lenta. No mínimo, isso significa que ele não será rápido de maneira confiável.

Diferentes abordagens para arquiteturas

Descobrir como responder às solicitações de navegação e evitar a rede pode ser complicado. A abordagem certa depende muito da arquitetura do site e do número de URLs únicos que os usuários podem acessar.

Embora não haja uma solução única para todos, as diretrizes gerais a seguir ajudam você a decidir qual abordagem é a mais viável.

Pequenos sites estáticos

Se seu aplicativo da Web consiste em um número relativamente pequeno (por exemplo, algumas dúzias) de URLs únicos, e cada um desses URLs corresponde a um arquivo HTML estático diferente, uma abordagem viável é armazenar todos esses arquivos HTML em cache e responder às solicitações de navegação com o HTML armazenado em cache apropriado.

Com o armazenamento em cache, é possível armazenar o HTML em cache com antecedência, assim que o service worker for instalado, e atualizar o HTML armazenado em cache sempre que você recriar seu site e reimplantar o service worker.

Como alternativa, se preferir evitar o pré-armazenamento em cache de todo o HTML, talvez porque os usuários tendem a navegar para apenas um subconjunto de URLs no site, use uma estratégia de armazenamento em cache do ambiente de execução desatualizado enquanto revalida. No entanto, tenha cuidado com essa abordagem, já que cada documento HTML individual é armazenado em cache e atualizado separadamente. Usar o armazenamento em cache do tempo de execução para HTML é mais apropriado se você tem um pequeno número de URLs que são acessados com frequência pelo mesmo grupo de usuários e se você se sente confortável em que esses URLs sejam revalidados de maneira independente uns dos outros.

Apps de uma única página

Uma arquitetura de página única é usada com frequência por aplicativos da Web modernos. Nele, o JavaScript do lado do cliente modifica o HTML em resposta a ações do usuário. Esse modelo usa a API History para modificar o URL atual à medida que o usuário interage com o app da Web, levando ao que efetivamente é uma navegação "simulada". Embora as próximas navegações possam ser "falsas", a navegação inicial é real, e ainda é importante garantir que ela não esteja bloqueada na rede.

Felizmente, se você estiver usando a arquitetura de página única, há um padrão simples a ser seguido para atender à navegação inicial do cache: o shell do aplicativo. Nesse modelo, o service worker responde a solicitações de navegação retornando o mesmo arquivo HTML que já foi pré-armazenado em cache, independentemente do URL solicitado. Esse HTML precisa ser simples e consistir, talvez, em um indicador de carregamento genérico ou em um conteúdo de esqueleto. Depois que o navegador carrega esse HTML do cache, o JavaScript atual do lado do cliente assume e renderiza o conteúdo HTML correto para o URL da solicitação de navegação original.

A caixa de trabalho oferece as ferramentas necessárias para implementar essa abordagem. O navigateFallback option permite que você especifique qual documento HTML usar como shell do app, junto com uma lista de permissões e negações opcionais para limitar esse comportamento a um subconjunto dos seus URLs.

Apps de várias páginas

Se o servidor da Web gera o HTML do site dinamicamente ou se você tem algumas dezenas de páginas exclusivas, é muito mais difícil evitar a rede ao processar as solicitações de navegação. As recomendações em Todas as demais provavelmente serão válidas para você.

No entanto, em um determinado subconjunto de apps de várias páginas, é possível implementar um service worker que replique totalmente a lógica usada no servidor da Web para gerar HTML. Isso funcionará melhor se você puder compartilhar informações de roteamento e modelos entre os ambientes do servidor e do service worker e, em particular, se o servidor da Web usar JavaScript (sem depender de recursos específicos do Node.js, como o acesso ao sistema de arquivos).

Caso seu servidor da Web se enquadre nessa categoria e você queira conhecer uma abordagem para mover a geração de HTML da rede para seu service worker, consulte as orientações em Além de SPAs: arquiteturas alternativas para seu PWA (em inglês).

Todos os outros

Se não for possível responder às solicitações de navegação com HTML armazenado em cache, tome medidas para garantir que a adição de um service worker ao seu site (para processar outras solicitações não HTML) não atrase a navegação. A inicialização do service worker sem usá-lo para responder a uma solicitação de navegação vai introduzir uma pequena quantidade de latência (como explicado em Como criar apps mais rápidos e resilientes com o service worker). É possível reduzir essa sobrecarga ativando um recurso chamado pré-carregamento de navegação e, em seguida, usando a resposta de rede que foi pré-carregada dentro do manipulador de eventos fetch.

O Workbox fornece uma biblioteca auxiliar que detecta se o pré-carregamento de navegação tem suporte e, em caso afirmativo, simplifica o processo de instruir o service worker a usar a resposta de rede.

Foto de Aaron Burden no Unsplash