Compartilhar via


Utilizar uma função de trabalho de serviço para gerir pedidos de rede

Uma função de trabalho de serviço é um tipo especial de trabalho web que consegue intercetar, modificar e responder a pedidos de rede com a Fetch API. Para armazenar recursos, uma função de trabalho de serviço pode aceder à Cache API e aceder a arquivos de dados assíncronos do lado do cliente, como IndexedDB.

Uma função de trabalho de serviço pode tornar o seu PWA mais rápido ao colocar os recursos em cache localmente e também pode tornar o seu PWA mais fiável ao permitir que funcione mesmo quando o dispositivo do utilizador está offline ou tem uma ligação de rede intermitente.

Se o seu PWA tiver uma função de trabalho de serviço, a função de trabalho de serviço é instalada na primeira vez que o utilizador aceder ao seu PWA. Em seguida, a função de trabalho de serviço é executada em paralelo com a sua aplicação e pode continuar a trabalhar mesmo quando a aplicação não está em execução.

Uma função de trabalho de serviço é responsável por intercetar, modificar e responder a pedidos de rede. A função de trabalho de serviço pode ser alertada quando a aplicação tenta carregar um recurso a partir do servidor ou quando a aplicação envia um pedido para obter dados do servidor. Quando isto acontece, a função de trabalho de serviço pode decidir deixar o pedido ir para o servidor ou intercetar o pedido e devolver uma resposta da cache.

Diagrama a mostrar a função de trabalho de serviço entre a aplicação e a rede e o armazenamento em cache

Registar uma função de trabalho de serviço

Semelhante a outras funções de trabalho da Web, um trabalhador de serviço tem de existir num ficheiro separado. O ficheiro contém uma única função de trabalho de serviço. Pode referenciar este ficheiro ao registar a função de trabalho de serviço, conforme mostrado no seguinte código:

if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/serviceworker.js");
}

O browser que está a executar o seu PWA pode fornecer diferentes níveis de suporte para a sua função de trabalho de serviço. Além disso, o contexto no qual o PWA está em execução pode não ser seguro. Por conseguinte, é uma boa prática testar a existência do objeto antes de navigator.serviceWorker executar qualquer código relacionado com a função de trabalho do serviço. No código acima, uma função de trabalho de serviço é registada através do serviceworker.js ficheiro localizado na raiz do site.

Certifique-se de que coloca o ficheiro de trabalho de serviço no diretório de nível mais alto que pretende que a função de trabalho de serviço faça a gestão. Este diretório é denominado âmbito da função de trabalho de serviço. No código anterior, o ficheiro é armazenado no diretório de raiz da sua aplicação e a função de trabalho de serviço gere todas as páginas que estão sob o nome de domínio da aplicação.

Se o ficheiro de trabalho de serviço tiver sido armazenado num js diretório, o âmbito da função de trabalho de serviço será limitado ao js diretório e a quaisquer subdiretórios. Como melhor prática, coloque o ficheiro de trabalho de serviço na raiz da sua aplicação, a menos que precise de reduzir o âmbito da sua função de trabalho de serviço.

Intercetar pedidos

O evento principal que utiliza numa função de trabalho de serviço é o fetch evento. O fetch evento é executado sempre que o browser que a sua aplicação executa tenta aceder ao conteúdo no âmbito da função de trabalho de serviço.

O código seguinte mostra como adicionar um serviço de escuta para o fetch evento:

self.addEventListener("fetch", event => {
  console.log("Fetching", event.request);
});

fetch No processador, pode controlar se um pedido vai para a rede, extrai da cache e assim sucessivamente. A abordagem que adotar irá provavelmente variar, com base no tipo de recurso que está a ser pedido, na frequência com que é atualizado e noutra lógica de negócio exclusiva da sua aplicação.

Eis alguns exemplos do que pode fazer no fetch processador:

  • Se disponível, devolva uma resposta da cache; caso contrário, peça o recurso através da rede.
  • Obtenha um recurso da rede, coloque uma cópia em cache e devolva a resposta.
  • Permitir que os utilizadores especifiquem uma preferência para guardar dados.
  • Forneça uma imagem de marcador de posição para determinados pedidos de imagem.
  • Gere uma resposta diretamente na função de trabalho de serviço.

O ciclo de vida da função de trabalho de serviço

O ciclo de vida de uma função de trabalho de serviço consiste em vários passos, com cada passo a acionar um evento. Pode adicionar serviços de escuta a estes eventos para executar código para efetuar uma ação. A lista seguinte apresenta uma vista de alto nível do ciclo de vida e dos eventos relacionados de uma função de trabalho de serviço:

  1. Registe a função de trabalho de serviço.

  2. O browser transfere o ficheiro JavaScript, instala a função de trabalho de serviço e aciona o install evento. Pode utilizar o install evento para colocar em pré-cache quaisquer ficheiros importantes e de longa duração (como ficheiros CSS, ficheiros JavaScript, imagens de logótipo ou páginas offline) da sua aplicação.

    self.addEventListener("install", event => {
      console.log("Install event in progress.");
    });
    
  3. A função de trabalho de serviço é ativada, o que aciona o activate evento. Utilize este evento para limpar caches desatualizadas.

    self.addEventListener("activate", event => {
      console.log("Activate event in progress.");
    });
    
  4. A função de trabalho de serviço está pronta para ser executada quando a página for atualizada ou quando o utilizador for para uma nova página no site. Se quiser executar a função de trabalho de serviço sem aguardar, utilize o self.skipWaiting() método durante o install evento, da seguinte forma:

    self.addEventListener("install", event => {
      self.skipWaiting();
    });
    
  5. A função de trabalho de serviço está agora em execução e pode ouvir fetch eventos.

Recursos de pré-cache

Quando um utilizador acede à sua aplicação pela primeira vez, se tiver definido uma função de trabalho de serviço, a função de trabalho de serviço da aplicação é instalada. Utilize o install evento no código de trabalho do serviço para detetar quando isto ocorre e coloque em cache todos os recursos estáticos de que a sua aplicação precisa. Colocar em cache os recursos estáticos da sua aplicação (como o CÓDIGO HTML, CSS e JavaScript) necessários para a página inicial permite que a aplicação seja executada mesmo quando o dispositivo do utilizador está offline.

Para colocar os recursos da aplicação em cache, utilize 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());
});

Tenha em atenção que, após a instalação inicial, o install evento não é executado novamente. Para atualizar o código da sua função de trabalho de serviço, consulte Atualizar a sua função de trabalho de serviço.

Agora, pode utilizar o fetch evento para devolver os recursos estáticos da cache, em vez de os carregar novamente a partir 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());
});

Por questões de brevidade, o exemplo de código acima não processa os casos em que a obtenção do URL do pedido da rede falhou.

Utilizar uma página offline personalizada

Quando a aplicação utiliza múltiplas páginas HTML, um cenário offline comum é redirecionar pedidos de navegação de páginas para uma página de erro personalizada quando o dispositivo do utilizador está 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 a função de trabalho de serviço

Instalar uma nova versão da função de trabalho de serviço

Se efetuar alterações ao código de trabalho de serviço e implementar o novo ficheiro de trabalho de serviço no servidor Web, os dispositivos dos seus utilizadores começarão gradualmente a utilizar a nova função de trabalho de serviço.

Sempre que um utilizador navega para uma das páginas da sua aplicação, o browser que está a executar a aplicação verifica se está disponível uma nova versão da função de trabalho de serviço no servidor. O browser deteta novas versões ao comparar os conteúdos entre a função de trabalho de serviço existente e a nova função de trabalho de serviço. Quando é detetada uma alteração, a nova função de trabalho de serviço é instalada ( install o evento é acionado) e, em seguida, a nova função de trabalho de serviço aguarda que a função de trabalho de serviço existente deixe de ser utilizada no dispositivo.

Na prática, isto significa que pode haver duas funções de trabalho de serviço em execução ao mesmo tempo, mas apenas a função de trabalho de serviço (original) existente interceta os pedidos de rede da aplicação. Quando a aplicação é fechada, a função de trabalho de serviço existente deixa de ser utilizada. Da próxima vez que a aplicação for aberta, a nova função de trabalho de serviço é ativada. O activate evento é acionado e a nova função de trabalho de serviço começa a intercetar fetch eventos.

Pode ativar com força a nova função de trabalho de serviço assim que estiver instalada, utilizando self.skipWaiting() no processador de eventos da sua função de trabalho de install serviço.

Para saber mais sobre como uma função de trabalho de serviço é atualizada, veja Atualizar a função de trabalho de serviço no web.dev.

Atualizar os ficheiros estáticos em cache

Ao colocar previamente recursos estáticos em cache, como ficheiros de folha de estilos CSS, conforme descrito em Recursos de pré-cache, a sua aplicação utiliza apenas as versões em cache dos ficheiros e não tenta transferir novas versões.

Para garantir que os utilizadores obtêm as alterações mais recentes aos recursos estáticos que são utilizados pela sua aplicação, utilize uma convenção de nomenclatura de cache e atualize o código de trabalho do serviço.

A colocação em cache significa que cada ficheiro estático tem o nome de acordo com a respetiva versão. Isto pode ser conseguido de várias formas, mas normalmente envolve a utilização de uma ferramenta de compilação que lê o conteúdo de um ficheiro e gera um ID exclusivo com base no conteúdo. Esse ID pode ser utilizado para atribuir um nome ao ficheiro estático em cache.

Em seguida, atualize o código da função de trabalho de serviço para colocar 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 fragmento de código acima e o dos recursos de Pré-cache. Quando esta nova função de trabalho de serviço é instalada, será criada uma nova cache e os novos recursos estáticos serão transferidos e colocados em cache. Quando a função de trabalho de serviço é ativada, a cache antiga será eliminada. Neste momento, o utilizador terá a nova versão da aplicação.

Por vezes, fazer alterações à sua função de trabalho de serviço pode ser complexa. Utilize uma biblioteca como a Caixa de Trabalho para simplificar o passo de compilação dos recursos estáticos e o código de trabalho do serviço.

Testar as ligações de rede no seu PWA

É útil saber quando uma ligação de rede está disponível para sincronizar dados ou informar os utilizadores de que o estado da rede foi alterado.

Utilize as seguintes opções para testar a conectividade de rede:

A navigator.onLine propriedade é um booleano que lhe permite saber o estado atual da rede. Se o valor for true, o utilizador está online; caso contrário, o utilizador está offline.

Para saber mais, veja navigator.onLine no MDN.

Eventos online e offline

Pode tomar medidas quando a conectividade de rede for alterada. Pode ouvir e tomar medidas em resposta a eventos de rede. Os eventos estão disponíveis nos windowelementos , documente document.body , conforme mostrado abaixo:

window.addEventListener("online",  function(){
    console.log("You are online!");
});
window.addEventListener("offline", function(){
    console.log("Network connection lost!");
});

Para saber mais, veja Navigator.onLine no MDN.

Outras capacidades

A principal responsabilidade de um trabalhador de serviço é tornar a sua aplicação mais rápida e fiável em caso de uma ligação de rede instável. Normalmente, uma função de trabalho de serviço utiliza o evento e Cache a fetch API para o fazer, mas uma função de trabalho de serviço pode utilizar outras APIs para cenários especializados, tais como:

  • Sincronização em segundo plano de dados.
  • Sincronização periódica de dados.
  • Transferências de ficheiros em segundo plano grandes.
  • Processamento e notificações de Mensagens push.

Sincronização em segundo plano

Utilize a API de Sincronização de Fundo para permitir que os utilizadores continuem a utilizar a sua aplicação e efetuem ações mesmo quando o dispositivo do utilizador está offline.

Por exemplo, uma aplicação de e-mail pode permitir que os respetivos utilizadores composem e enviem mensagens em qualquer altura. O front-end da aplicação pode tentar enviar a mensagem imediatamente e, se o dispositivo estiver offline, a função de trabalho de serviço pode detetar o pedido falhado e utilizar a API de Sincronização de Fundo para adiar a tarefa até estar ligado.

Para saber mais, veja Utilizar a API de Sincronização de Segundo Plano para sincronizar dados com o servidor.

Sincronização em segundo plano do período

A API de Sincronização de Fundo Periódica permite que os PWAs obtenham conteúdos novos periodicamente, em segundo plano, para que os utilizadores possam aceder imediatamente ao conteúdo quando abrirem a aplicação novamente mais tarde.

Ao utilizar a API de Sincronização de Fundo Periódica, as PWAs não têm de transferir novos conteúdos (como novos artigos) enquanto o utilizador estiver a utilizar a aplicação. A transferência de conteúdos pode tornar a experiência mais lenta, pelo que, em vez disso, a aplicação pode obter os conteúdos numa altura mais conveniente.

Para saber mais, veja Utilizar a API de Sincronização de Fundo Periódica para obter regularmente conteúdos novos.

Transferências de ficheiros em segundo plano grandes

A API de Obtenção em Segundo Plano permite que os PWAs deleguem completamente a transferência de grandes quantidades de dados para o motor do browser. Desta forma, a aplicação e a função de trabalho de serviço não têm de estar em execução enquanto a transferência estiver em curso.

Esta API é útil para aplicações que permitem aos utilizadores transferir ficheiros grandes (como música, filmes ou podcasts) para casos de utilização offline. A transferência é delegada ao motor do browser, que sabe como lidar com uma ligação intermitente ou até mesmo uma perda completa de conectividade.

Para saber mais, veja Utilizar a API de Obtenção em Segundo Plano para obter ficheiros grandes quando a aplicação ou a função de trabalho de serviço não estiver em execução.

Enviar mensagens

As mensagens push podem ser enviadas aos seus utilizadores sem que tenham de utilizar a aplicação no momento. Uma função de trabalho de serviço pode ouvir mensagens push enviadas pelo servidor mesmo que a aplicação não esteja em execução e apresentar uma notificação no centro de notificações do sistema operativo.

Para saber mais, veja Voltar a envolver os utilizadores com mensagens push.

Depurar com DevTools

Com o Microsoft Edge DevTools, pode ver se a sua função de trabalho de serviço foi registada corretamente e ver em que estado de ciclo de vida a função de trabalho de serviço está atualmente. Além disso, pode depurar o código JavaScript na sua função de trabalho de serviço.

Para saber mais, veja Depurar a sua função de trabalho de serviço.

Confira também