Usar o Service Workers para gerenciar solicitações de rede
Os Trabalhadores de Serviço são um tipo especial de Web Workers com a capacidade de interceptar, modificar e responder a solicitações de rede usando a Fetch
API. Os Trabalhadores de Serviço podem acessar a API e os Cache
armazenamentos de dados assíncronos do lado do cliente, como IndexedDB
, para armazenar recursos.
Os Trabalhadores de Serviço podem tornar seu PWA mais rápido, armazenando recursos localmente, e eles também podem tornar seu PWA mais confiável, tornando-o independente de rede.
Na primeira vez que um usuário acessa seu PWA, o Service Worker é instalado. Em seguida, o Service Worker é executado em paralelo ao seu aplicativo e pode continuar trabalhando mesmo quando seu aplicativo não estiver em execução.
Os Trabalhadores de Serviço são responsáveis por interceptar, modificar e responder a solicitações de rede. Eles podem ser alertados quando o aplicativo tenta carregar um recurso do servidor ou envia uma solicitação para obter dados do servidor. Quando isso acontece, um Service Worker pode decidir deixar a solicitação ir para o servidor ou interceptá-la e retornar uma resposta do cache.
Registrar um trabalhador de serviço
Semelhante a outros Trabalhos Web, os Trabalhadores de Serviço devem existir em um arquivo separado. Você faz referência a esse arquivo ao registrar o Service Worker, conforme mostrado no seguinte código:
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/serviceworker.js");
}
O navegador da Web que está executando seu PWA pode fornecer níveis diferentes de suporte para os Trabalhadores de Serviço. Além disso, o contexto em que sua PWA está em execução pode não estar seguro. Como tal, é uma boa prática testar a existência do objeto antes de navigator.serviceWorker
executar qualquer código relacionado ao Service Worker. No código acima, um Service Worker é registrado usando o serviceworker.js
arquivo localizado na raiz do site.
Certifique-se de colocar o arquivo do Service Worker no diretório de nível mais alto que você deseja que ele gerencie. Esse diretório é chamado de escopo do Service Worker. No código anterior, o arquivo é armazenado no diretório raiz do seu aplicativo e o Service Worker gerencia todas as páginas que estão sob o nome de domínio do aplicativo.
Se o arquivo Do Trabalho de Serviço fosse armazenado em um js
diretório, o escopo do Service Worker seria limitado ao diretório e a js
quaisquer subdiretórios. Como prática recomendada, coloque o arquivo Do Trabalho de Serviço na raiz do seu aplicativo, a menos que você precise reduzir o escopo do Trabalho de Serviço.
Interceptar solicitações
O evento main que você usa em um Service Worker é o fetch
evento. O fetch
evento é executado sempre que o navegador que seu aplicativo executa na tentativa de acessar o conteúdo no escopo do Service Worker.
O código a seguir mostra como adicionar um ouvinte para o fetch
evento:
self.addEventListener("fetch", event => {
console.log('WORKER: Fetching', event.request);
});
fetch
No manipulador, você pode controlar se uma solicitação vai para a rede, é retirada do cache e assim por diante. A abordagem que você adotar provavelmente variará, com base no tipo de recurso que está sendo solicitado, com que frequência ele é atualizado e outra lógica de negócios exclusiva do seu aplicativo.
Aqui estão alguns exemplos do que você pode fazer no fetch
manipulador:
- Se estiver disponível, retorne uma resposta do cache; caso contrário, fallback para solicitar o recurso pela rede.
- Buscar um recurso da rede, armazenar em cache uma cópia e retornar a resposta.
- Permitir que os usuários especifiquem uma preferência para salvar dados.
- Forneça uma imagem de espaço reservado para determinadas solicitações de imagem.
- Gere uma resposta diretamente no Service Worker.
O ciclo de vida do Service Worker
O ciclo de vida de um Service Worker consiste em várias etapas, com cada etapa disparando um evento. Você pode adicionar ouvintes a esses eventos para executar o código para executar uma ação. A lista a seguir apresenta uma exibição de alto nível do ciclo de vida e eventos relacionados dos Trabalhadores de Serviço.
Registre o Trabalho de Serviço.
O navegador baixa o arquivo JavaScript, instala o Service Worker e dispara o
install
evento. Você pode usar oinstall
evento para pré-armazenar em cache arquivos importantes e de longa duração (como arquivos CSS, arquivos JavaScript, imagens de logotipo ou páginas offline) do seu aplicativo.self.addEventListener("install", event => { console.log("WORKER: install event in progress."); });
O Service Worker é ativado, o que dispara o
activate
evento. Use esse evento para limpo caches desatualizados.self.addEventListener("activate", event => { console.log("WORKER: activate event in progress."); });
O Service Worker está pronto para ser executado quando a página for atualizada ou quando o usuário for para uma nova página no site. Se você quiser executar o Service Worker sem aguardar, use o
self.skipWaiting()
método durante oinstall
evento, da seguinte maneira:self.addEventListener("install", event => { self.skipWaiting(); // … });
O Trabalho de Serviço agora está em execução e pode ouvir
fetch
eventos.
Recursos de pré-cache
Quando um usuário acessa seu aplicativo pela primeira vez, o Service Worker do aplicativo é instalado. Use o install
evento em seu Service Worker para detectar quando isso ocorrer e armazenar em cache todos os recursos estáticos que seu aplicativo precisa. O cache dos recursos estáticos do seu aplicativo (como o código HTML, CSS e JavaScript) necessários pela página inicial permite que seu aplicativo seja executado mesmo quando o dispositivo do usuário está offline.
Para armazenar em cache os recursos do aplicativo, use o objeto global caches
e o cache.addAll
método, conforme mostrado abaixo:
// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js"];
// Listen to the `install` event.
self.addEventListener("install", event => {
async function preCacheResources() {
// Open the app's cache.
const cache = await caches.open(CACHE_NAME);
// Cache all static resources.
cache.addAll(PRE_CACHED_RESOURCES);
}
event.waitUntil(preCacheResources());
});
Observe que, após a instalação inicial, o install
evento não será executado novamente. Para atualizar o código do Service Worker, consulte Atualizar seu Trabalho de Serviço.
Agora você pode usar o fetch
evento para retornar os recursos estáticos do cache, em vez de carregá-los novamente da rede:
self.addEventListener("fetch", event => {
async function returnCachedResource() {
// Open the app's cache.
const cache = await caches.open(CACHE_NAME);
// Find the response that was pre-cached during the `install` event.
const cachedResponse = await cache.match(event.request.url);
if (cachedResponse) {
// Return the resource.
return cachedResponse;
} else {
// The resource wasn't found in the cache, so fetch it from the network.
const fetchResponse = await fetch(event.request.url);
// Put the response in cache.
cache.put(event.request.url, fetchResponse.clone());
// And return the response.
return fetchResponse.
}
}
event.respondWith(returnCachedResource());
});
Para brevidade, o exemplo de código acima não manipula os casos em que falha ao obter a URL de solicitação da rede.
Usar uma página offline personalizada
Quando seu aplicativo usa várias páginas HTML, um cenário offline comum é redirecionar solicitações de navegação de página para uma página de erro personalizada quando o dispositivo do usuário estiver offline:
// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
// Note the offline page in this list.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js", "/offline"];
// Listen to the `install` event.
self.addEventListener("install", event => {
async function preCacheResources() {
// Open the app's cache.
const cache = await caches.open(CACHE_NAME);
// Cache all static resources.
cache.addAll(PRE_CACHED_RESOURCES);
}
event.waitUntil(preCacheResources());
});
self.addEventListener("fetch", event => {
async function navigateOrDisplayOfflinePage() {
try {
// Try to load the page from the network.
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// The network call failed, the device is offline.
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match("/offline");
return cachedResponse;
}
}
// Only call event.respondWith() if this is a navigation request
// for an HTML page.
if (event.request.mode === 'navigate') {
event.respondWith(navigateOrDisplayOfflinePage());
}
});
Atualizar seu Trabalho de Serviço
Instalar uma nova versão do Service Worker
Se você fizer alterações no código do Service Worker e implantar o novo arquivo do Service Worker no servidor Web, os dispositivos dos usuários começarão gradualmente a usar o novo Service Worker.
Sempre que um usuário navega até uma das páginas do seu aplicativo, o navegador que está executando o aplicativo verifica se uma nova versão do Service Worker está disponível no servidor. O navegador detecta novas versões comparando o conteúdo entre o Trabalho de Serviço existente e o novo Service Worker. Quando uma alteração é detectada, o novo Service Worker é instalado (o install
evento é disparado) e, em seguida, o novo Service Worker aguarda que o Trabalhador de Serviço existente pare de ser usado no dispositivo.
Na prática, isso significa que pode haver dois Trabalhadores de Serviço em execução ao mesmo tempo, mas apenas um intercepta as solicitações de rede do aplicativo. Quando o aplicativo é fechado, o Trabalho de Serviço existente deixa de ser usado. Na próxima vez que o aplicativo for aberto, o novo Service Worker será ativado. O activate
evento é disparado e o novo Service Worker inicia a interceptação de fetch
eventos.
Você pode ativar com força o novo Service Worker assim que ele for instalado, usando self.skipWaiting()
no manipulador de eventos do install
Service Worker.
Para saber mais sobre como os Trabalhadores do Serviço são atualizados, consulte Atualizando o Trabalho de Serviço no web.dev.
Atualizar seus arquivos estáticos armazenados em cache
Ao pré-armazenar recursos estáticos, como arquivos de planilha de estilos CSS, conforme descrito em recursos de pré-cache, seu aplicativo usa apenas as versões armazenadas em cache dos arquivos e não tenta baixar novas versões.
Para garantir que os usuários obtenham as alterações mais recentes nos recursos estáticos usados pelo seu aplicativo, use uma convenção de nomenclatura de quebra de cache e atualize seu código do Service Worker.
A quebra de cache significa que cada arquivo estático é nomeado de acordo com sua versão. Isso pode ser obtido de várias maneiras, mas geralmente envolve o uso de uma ferramenta de build que lê o conteúdo de um arquivo e gera uma ID exclusiva com base no conteúdo. Essa ID pode ser usada para nomear o arquivo estático armazenado em cache.
Em seguida, atualize o código do Service Worker para armazenar em cache os novos recursos estáticos durante install
:
// The name of the new cache your app uses.
const CACHE_NAME = "my-app-cache-v2";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles-124656.css", "app-576391.js"];
// Listen to the `install` event.
self.addEventListener("install", event => {
async function preCacheResources() {
// Open the app's cache.
const cache = await caches.open(CACHE_NAME);
// Cache the new static resources.
cache.addAll(PRE_CACHED_RESOURCES);
}
event.waitUntil(preCacheResources());
});
// Listen to the `activate` event to clear old caches.
self.addEventListener("activate", event => {
async function deleteOldCaches() {
// List all caches by their names.
const names = await caches.keys();
await Promise.all(names.map(name => {
if (name !== CACHE_NAME) {
// If a cache's name is the current name, delete it.
return caches.delete(name);
}
}));
}
event.waitUntil(deleteOldCaches());
});
Compare os CACHE_NAME
valores e PRE_CACHED_RESOURCES
entre o snippet de código acima e o dos recursos pré-cache. Quando esse novo Service Worker for instalado, um novo cache será criado e os novos recursos estáticos serão baixados e armazenados em cache. Quando o Service Worker for ativado, o cache antigo será excluído. Neste ponto, o usuário terá a nova versão do aplicativo.
Às vezes, fazer alterações no Trabalho de Serviço pode ser complexo. Use uma biblioteca como a Workbox para simplificar a etapa de compilação de recursos estáticos e o código do Service Worker.
Teste para conexões de rede em seu PWA
É útil saber quando uma conexão de rede está disponível, a fim de sincronizar dados ou informar aos usuários que a rede status foi alterada.
Use as seguintes opções para testar a conectividade de rede:
navigator.onLine
A navigator.onLine
propriedade é um booliano que permite que você saiba o status atual da rede. Se o valor for true
, o usuário estará online; caso contrário, o usuário estará offline.
Para saber mais, consulte navigator.onLine no MDN.
Eventos online e offline
Você pode agir quando a conectividade de rede for alterada. Você pode ouvir e tomar medidas em resposta a eventos de rede. Os eventos estão disponíveis nos window
elementos , document
e document.body
, conforme mostrado abaixo:
window.addEventListener("online", function(){
console.log("You are online!");
});
window.addEventListener("offline", function(){
console.log("Oh no, you lost your network connection.");
});
Para saber mais, consulte Navigator.onLine no MDN.
Outras funcionalidades
A responsabilidade main do Service Worker é tornar seu aplicativo mais rápido e confiável no caso de uma conexão de rede instável. Os Trabalhadores de Serviço usam principalmente o fetch
evento e Cache
a API para fazer isso, mas podem usar outras APIs para cenários avançados, como:
- Sincronização em segundo plano de dados.
- Sincronização periódica de dados.
- Downloads de arquivos em segundo plano grandes.
- Tratamento e notificações de mensagens push.
Sincronização em segundo plano
Use a API de Sincronização em Segundo Plano para permitir que os usuários continuem usando seu aplicativo e executem ações mesmo quando o dispositivo do usuário estiver offline.
Por exemplo, um aplicativo de email pode permitir que seus usuários componham e enviem mensagens a qualquer momento. O front-end do aplicativo pode tentar enviar a mensagem imediatamente e, se o dispositivo estiver offline, o Service Worker poderá capturar a solicitação com falha e usar a API de Sincronização em Segundo Plano para adiar a tarefa até estar conectado.
Para saber mais, confira Usar a API de Sincronização em Segundo Plano para sincronizar dados com o servidor.
Sincronização em segundo plano de período
A API de Sincronização de Antecedentes Periódicos permite que PWAs recuperem conteúdo novo periodicamente, em segundo plano, para que os usuários possam acessar imediatamente o conteúdo quando abrirem o aplicativo novamente.
Usando a API de Sincronização de Antecedentes Periódicos, as PWAs não precisam baixar novos conteúdos (como novos artigos) enquanto o usuário estiver usando o aplicativo. Baixar conteúdo pode retardar a experiência, portanto, o aplicativo pode recuperar o conteúdo em um momento mais conveniente.
Para saber mais, confira Usar a API de Sincronização de Planos de Fundo Periódico para obter conteúdo recente regularmente.
Downloads de arquivos em segundo plano grandes
A API de Busca em Segundo Plano permite que PWAs delegam completamente o download de grandes quantidades de dados para o mecanismo do navegador. Dessa forma, o aplicativo e o Service Worker não precisam estar em execução enquanto o download estiver em andamento.
Essa API é útil para aplicativos que permitem que os usuários baixem arquivos grandes (como música, filmes ou podcasts) para casos de uso offline. O download é delegado ao mecanismo do navegador, que sabe como lidar com uma conexão intermitente ou até mesmo uma perda completa de conectividade.
Para saber mais, confira Usar a API de Busca em Segundo Plano para buscar arquivos grandes quando o aplicativo ou o Service Worker não estiver em execução.
Enviar mensagens por push
Mensagens push podem ser enviadas para seus usuários sem que eles precisem usar o aplicativo no momento. Um Service Worker pode ouvir mensagens por push enviadas pelo servidor mesmo que o aplicativo não esteja em execução e exibir uma notificação na central de notificação do sistema operacional.
Para saber mais, consulte Reengajamento de usuários com mensagens push.
Depuração com DevTools
Usando o Microsoft Edge DevTools, você pode ver se o Service Worker foi registrado corretamente e ver em qual estado de ciclo de vida o Service Worker está atualmente. Além disso, você pode depurar o código JavaScript em seu Service Worker.
Para saber mais, confira Depurar seu Trabalho de Serviço.