Como o JavaScript funciona: Service Workers, seu ciclo de vida e casos de uso
Este é o post #8 da série dedicada a explorar JavaScript e seus componentes de construção. No processo de identificação e descrição dos elementos principais, também compartilhamos algumas das melhores práticas que usamos ao criar o SessionStack , um aplicativo JavaScript que precisa ser robusto e de alto desempenho para mostrar a você exatamente em tempo real como seus usuários se depararam com um problema técnico ou UX no seu aplicativo da web.
Abaixo estão os posts dessa série publicados até o momento
- Uma visão geral do mecanismo, o tempo de execução e a pilha de chamadas
- Dentro do motor V8 do Google + 5 dicas sobre como escrever código otimizado
- Gerenciamento de memória + como lidar com 4 vazamentos comuns de memória
- O Event loop e o surgimento da programação assíncrona + 5 maneiras de codificar melhor com async/await
- Como o JavaScript funciona: Aprofundando em WebSockets e HTTP/2 com SSE + como escolher o caminho certo
- Como funciona o JavaScript: uma comparação com o WebAssembly + por quê, em certos casos, é melhor usá-lo sobre JavaScript
- Como o JavaScript funciona: Os blocos de construção dos Web Workers + 5 casos quando você deve usá-los
Você provavelmente já sabe que os Progressive Web Apps estarão cada vez mais populares, já que visam tornar a experiência do usuário de aplicativos da Web mais tranquila, criando experiências nativas semelhantes a aplicativos, em vez da aparência e comportamento do navegador.
Um dos principais requisitos para criar um Progressive Web App é torná-lo muito confiável em termos de rede e carregamento — ele deve ser utilizável em condições de rede incertas ou inexistentes.
Neste post, vamos nos aprofundar nos Service Workers: como eles funcionam e com o que você deve se preocupar. No final, também listamos alguns benefícios exclusivos dos Service Workers dos quais você deve se beneficiar e compartilhe a experiência de nossa equipe aqui no SessionStack .
Visão geral
Se você quiser entender tudo sobre os Service Workers, você deve começar lendo nosso post em Web Workers .
Basicamente, o Service Worker é um tipo de Web Worker e, mais especificamente, é como um Shared Worker :
- O Service Worker é executado em seu próprio contexto de script global
- Não está vinculado a uma página da Web específica
- Não pode acessar o DOM
Um dos principais motivos pelos quais a Service Worker API é tão empolgante é que ela permite que seus aplicativos da Web suportem experiências off-line, oferecendo aos desenvolvedores controle total sobre o fluxo.
Ciclo de vida de um trabalhador de serviço
O ciclo de vida de um Service Worker é completamente separado da sua página da web. Consiste nas seguintes fases:
- Baixar
- Instalação
- Ativação
Baixar
É quando o navegador faz o download do arquivo .js
que contém o Service Worker.
Instalação
Para instalar um Service Worker para seu aplicativo da Web, você deve registrá-lo primeiro, o que pode ser feito em seu código JavaScript. Quando um Service Worker é registrado, ele solicita ao navegador que inicie uma etapa de instalação do Service Worker em segundo plano.
Ao registrar o Service Worker, você diz ao navegador onde reside o arquivo JavaScript do Service Worker. Vamos ver o seguinte código:
O código verifica se a API do Service Worker é suportada no ambiente atual. Se estiver, o Service Worker /sw.js
está registrado.
Você pode chamar o método register()
sempre que uma página for carregada sem preocupação - o navegador descobrirá se o funcionário do serviço já foi registrado e o manipulará adequadamente.
Um detalhe importante do método register()
é o local do arquivo do service worker. Nesse caso, você pode ver que o arquivo do service worker está na raiz do domínio. Isso significa que o escopo do prestador de serviços será a origem inteira. Em outras palavras, esse service worker receberá eventos de fetch
(que discutiremos mais adiante) para tudo nesse domínio. Se registrarmos o arquivo do service worker em /example/sw.js
, o service worker só verá eventos de fetch
para páginas cujos URLs começam com /example/
( /example/page1/
, /example/page2/
).
Durante a fase de instalação, é melhor carregar e armazenar em cache alguns ativos estáticos. Depois que os recursos são armazenados em cache com êxito, a instalação do Service Worker é concluída. Se não (o carregamento falha) — o Service Worker fará uma nova tentativa. Uma vez instalado com sucesso, você saberá que os ativos estáticos estão no cache.
Isso responde à sua pergunta se o registro precisar acontecer depois do evento de carregamento. Não é uma obrigação, mas é definitivamente recomendado.
Por quê? Vamos considerar a primeira visita de um usuário ao seu aplicativo da web. Ainda não há service worker, e o navegador não tem como saber antecipadamente se haverá um service worker que eventualmente será instalado. Se o Service Worker for instalado, o navegador precisará gastar CPU e memória extras para esse encadeamento adicional, caso contrário, o navegador gastará na renderização da página da Web.
O importante é que, se você simplesmente instalar um Service Worker em sua página, estará correndo o risco de atrasar o carregamento e a renderização — não tornando a página disponível para seus usuários o mais rápido possível.
Observe que isso é importante apenas para a primeira visita à página. Visitas de páginas subseqüentes não são afetadas pela instalação do Service Worker. Quando um Service Worker é ativado em uma primeira visita à página, ele pode manipular eventos de carregamento/armazenamento em cache para visitas subsequentes ao seu aplicativo da web. Tudo isso faz sentido, porque precisa estar pronto para lidar com conectividade de rede limitada.
Ativação
Depois que o Service Worker for instalado, a próxima etapa será sua ativação.Esta etapa é uma ótima oportunidade para gerenciar caches anteriores.
Uma vez ativado, o Service Worker começará a controlar todas as páginas que estiverem no escopo. Um fato interessante: a página que registrou o Service Worker pela primeira vez não será controlada até que a página seja carregada novamente. Depois que o Service Worker estiver no controle, ele estará em um dos seguintes estados:
- Ele manipulará os eventos de busca e mensagem que ocorrem quando uma solicitação ou mensagem de rede é feita a partir da página
- Será terminado para economizar memória
Aqui está como o ciclo de vida será semelhante:
Manipulando a instalação dentro do Service Worker
Depois que uma página girar o processo de registro, vamos ver o que acontece dentro do script do Service Worker, que manipula o evento de install
adicionando um ouvinte de evento à instância do Service Worker.
Essas são as etapas que precisam ser tomadas quando o evento de install
é tratado:
- Abra um cache
- Faça o cache de nossos arquivos
- Confirme se todos os ativos necessários estão em cache
Aqui está como uma instalação simples pode parecer dentro de um Service Worker:
Se todos os arquivos forem armazenados em cache com êxito, o service worker será instalado. Se algum dos arquivos não for baixado, a etapa de instalação falhará. Portanto, tenha cuidado com os arquivos que você coloca lá.
O tratamento do evento de install
é completamente opcional e você pode evitá-lo. Nesse caso, não é necessário executar nenhuma das etapas aqui.
Solicitações de armazenamento em cache durante o tempo de execução
Esta parte é o negócio real. É aqui que você verá como interceptar solicitações e retornar os caches criados (e criar novos).
Depois que um Service Worker é instalado e o usuário navega para outra página ou atualiza a página em que está, o Service Worker receberá eventos de busca. Aqui está um exemplo que demonstra como retornar ativos em cache ou executar uma nova solicitação e, em seguida, armazenar em cache o resultado:
Aqui está o que acontece em poucas palavras:
- O
event.respondWith()
determinará como responderemos ao evento defetch
. Nós passamos uma promessa decaches.match()
que olha para o pedido e descobre se há algum resultado em cache de qualquer um dos caches que foram criados. - Se houver um cache, a resposta será recuperada.
- Caso contrário, uma
fetch
será executada. - Verifique se o status é
200
. Também verificamos se o tipo de resposta é básico, o que indica que é uma solicitação de nossa origem. Solicitações para recursos de terceiros não serão armazenadas em cache neste caso. - A resposta é adicionada ao cache.
Solicitações e respostas precisam ser clonadas porque são streams . O corpo de uma stream pode ser consumido apenas uma vez. E como queremos consumi-los, queremos cloná-los, porque o navegador também precisa consumi-los.
Atualizando um Service Worker
Quando um usuário visita seu aplicativo da Web, o navegador tenta baixar novamente o arquivo .js
que contém seu código do Service Worker. Isso ocorre em segundo plano.
Se houver até mesmo uma diferença de byte única no arquivo do Service Worker que foi baixado agora em comparação com o arquivo atual do Service Worker, o navegador presumirá que há uma alteração e que um novo Service Worker deve ser iniciado.
O novo Service Worker será iniciado e o evento de instalação será acionado.Neste ponto, no entanto, o antigo Service Worker ainda está controlando as páginas do seu aplicativo da Web, o que significa que o novo Service Worker entrará no estado de waiting
.
Quando as páginas abertas no momento do seu aplicativo da Web forem fechadas, o antigo Service Worker será eliminado pelo navegador e o Service Worker recém-instalado assumirá o controle total. É quando seu evento de ativação será disparado.
Por que tudo isso é necessário? Para evitar o problema de ter duas versões de um aplicativo da Web em execução simultaneamente, em guias diferentes — algo que é realmente muito comum na web e pode criar erros realmente ruins (por exemplo, casos em que você tem esquema diferente ao armazenar dados localmente no navegador ).
Excluindo dados do cache
A etapa mais comum no retorno de chamada de activate
é o gerenciamento de cache. Você gostaria de fazer isso agora, porque se você tiver eliminado todos os caches antigos na etapa de instalação, os Service Workers antigos deixarão de ser capazes de exibir arquivos desse cache.
Aqui está um exemplo de como você pode excluir alguns arquivos do cache que não estão na lista de permissões (neste caso, tendo a page-1
ou a page-2
sob seus nomes):
Requisito HTTPS
Ao criar seu aplicativo da web, você poderá usar os Service Workers por meio do localhost, mas depois de implantá-lo na produção, será necessário ter HTTPS pronto (e esse é o último motivo para ter o HTTPS).
Usando um Service Worker, você pode seqüestrar conexões e fabricar respostas. Por não usar HTTPs, seu aplicativo da Web se torna propenso a ataques de intermediários .
Para tornar as coisas mais seguras, é necessário registrar os Trabalhadores do Serviço em páginas que são fornecidas por HTTPS, para que você saiba que o Service Worker que o navegador recebe não foi modificado durante a viagem pela rede.
Suporte de Navegador
O suporte do navegador para os Trabalhadores do Serviço está ficando melhor:
Você pode acompanhar o progresso de todos os navegadores aqui — https://jakearchibald.github.io/isserviceworkerready/ .
Service Workers estão abrindo as portas para ótimos recursos
Alguns recursos exclusivos que um Service Worker fornece são:
- Notificações push — permitem que os usuários aceitem atualizações oportunas de aplicativos da web.
- Sincronização em segundo plano — permite adiar ações até que o usuário tenha conectividade estável. Dessa forma, você pode ter certeza de que o que quer que o usuário queira enviar, seja realmente enviado.
- Sincronização periódica (futuro) — API que fornece funcionalidade para gerenciar a sincronização periódica de segundo plano.
- Geofencing (future) — você pode definir parâmetros, também conhecidos como geofences que circundam as áreas de interesse. O aplicativo da web recebe uma notificação quando o dispositivo cruza uma geofence, o que permite que você forneça uma experiência útil com base na geografia do usuário.
Cada um deles será discutido em detalhes em futuros posts desta série.
Estamos trabalhando constantemente para tornar o UX do SessionStack o mais suave possível, otimizando o tempo de carregamento e resposta da página.
Quando você reproduz uma sessão de usuário no SessionStack (ou assiste em tempo real), o front-end do SessionStack estará constantemente puxando dados de nossos servidores para criar uma experiência de buffering sem problemas para você. Para lhe dar um pouco de experiência — uma vez que você integra a biblioteca do SessionStack no seu aplicativo da web, ele estará continuamente coletando dados como alterações do DOM, interações do usuário, solicitações de rede, exceções não tratadas e mensagens de depuração.
Quando uma sessão é reproduzida ou transmitida em tempo real, o SessionStack atende a todos os dados, permitindo que você veja tudo o que o usuário experimentou em seu próprio navegador (visual e tecnicamente).Tudo isso precisa acontecer rapidamente, já que não queremos que os usuários esperem.
Como os dados são puxados pelo nosso front-end, esse é um ótimo lugar onde os Service Workers podem ser aproveitados para lidar com situações como recarregar o player e ter que transmitir tudo novamente. Lidar com conectividade de rede lenta também é muito importante.
Existe um plano gratuito se você quiser experimentar o SessionStack .
Recursos
- https://developers.google.com/web/fundamentals/primers/service-workers/
- https://github.com/w3c/ServiceWorker/blob/master/explainer.md
Este é um artigo traduzido com a autorização do autor. O artigo original pode ser lido em https://blog.sessionstack.com/how-javascript-works-service-workers-their-life-cycle-and-use-cases-52b19ad98b58
Autor do post original — Alexander Zlatkov — Co-founder & CEO @SessionStack