Como usar plug-ins

Ao usar o Workbox, talvez você queira manipular uma solicitação e uma resposta enquanto são buscadas ou armazenadas em cache. Os plug-ins do Workbox permitem adicionar comportamentos adicionais ao service worker com um mínimo de boilerplate extra. Eles podem ser empacotados e reutilizados em seus próprios projetos ou lançados publicamente para outras pessoas usarem.

O Workbox oferece vários plug-ins prontos para uso. Se você for do tipo criativo, poderá criar plug-ins personalizados de acordo com os requisitos do seu aplicativo.

Plug-ins do Workbox disponíveis

O Workbox oferece os seguintes plug-ins oficiais para uso no seu worker de serviço:

  • BackgroundSyncPlugin: se uma solicitação de rede falhar, o plug-in permite que você a adicione a uma fila de sincronização em segundo plano para ser solicitada novamente quando o próximo evento de sincronização for acionado.
  • BroadcastUpdatePlugin: permite enviar uma mensagem em um canal de transmissão ou por postMessage() sempre que um cache for atualizado.
  • CacheableResponsePlugin: apenas solicitações de cache que atendem a critérios específicos.
  • ExpirationPlugin: gerencia o número e a idade máxima dos itens em um cache.
  • RangeRequestsPlugin: responde a solicitações que incluem um cabeçalho de solicitação HTTP Range.

Os plug-ins da caixa de trabalho, um dos listados acima ou um personalizado, são usados com uma estratégia do Workbox ao adicionar uma instância do plug-in à propriedade plugins da estratégia:

import {registerRoute} from 'workbox-routing';
import {CacheFirst} from 'workbox-strategies';
import {ExpirationPlugin} from 'workbox-expiration';

registerRoute(
  ({request}) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
      }),
    ],
  })
);

Métodos para plug-ins personalizados

Um plug-in do Workbox precisa implementar uma ou mais funções de callback. Quando você adiciona um plug-in a uma estratégia, as funções de callback são executadas automaticamente no momento certo. A estratégia transmite informações relevantes para a função de callback sobre a solicitação e/ou resposta atual, ao seu plug-in o contexto necessário para agir. As seguintes funções de callback são compatíveis:

  • cacheWillUpdate: é chamada antes que um Response seja usado para atualizar um cache. Nesse método, a resposta pode ser modificada antes de ser adicionada ao cache ou é possível retornar null para evitar a atualização completa do cache.
  • cacheDidUpdate: chamado quando uma nova entrada é adicionada a um cache ou se uma entrada atual é atualizada. Os plug-ins que usam esse método podem ser úteis quando você quer realizar uma ação após uma atualização de cache.
  • cacheKeyWillBeUsed: chamado antes que uma solicitação seja usada como uma chave de cache. Isso ocorre para pesquisas de cache (quando mode é 'read') e gravações de cache (quando mode é 'write'). Esse callback é útil se você precisar substituir ou normalizar URLs antes de usá-los para acessar caches.
  • cachedResponseWillBeUsed: é chamado pouco antes de uma resposta de um cache ser usada, o que permite examinar essa resposta. Nesse momento, você pode retornar uma resposta diferente ou retornar null.
  • requestWillFetch: é chamado sempre que uma solicitação está prestes a ser enviada à rede. Útil quando você precisa alterar o Request pouco antes de ele ir para a rede.
  • fetchDidFail: é chamado quando uma solicitação de rede falha, provavelmente devido à ausência de conectividade de rede, e não é acionado quando o navegador tem uma conexão de rede, mas recebe um erro (por exemplo, 404 Not Found).
  • fetchDidSucceed: é chamada sempre que uma solicitação de rede é bem-sucedida, independentemente do código de resposta HTTP.
  • handlerWillStart: é chamado antes que qualquer lógica de gerenciador comece a ser executada. Isso é útil se você precisar definir o estado inicial do gerenciador. Por exemplo, se você quiser saber quanto tempo o gerenciador levou para gerar uma resposta, anote o horário de início nesse callback.
  • handlerWillRespond: chamado antes do método handle() da estratégia retornar uma resposta, o que será útil se você precisar modificar uma resposta antes de retorná-la a uma RouteHandler ou outra lógica personalizada.
  • handlerDidRespond: é chamado depois que o método handle() da estratégia retorna uma resposta. É quando pode ser útil registrar todos os detalhes da resposta final (por exemplo, após alterações feitas por outros plug-ins).
  • handlerDidComplete: é chamado depois que todas as promessas de tempo de vida estendidas adicionadas ao evento da invocação da estratégia foram resolvidas. Isso é útil se você precisar gerar relatórios sobre dados que precisam esperar até que o gerenciador seja concluído para calcular coisas como status de ocorrência em cache, latência de cache, latência de rede e outras informações úteis.
  • handlerDidError: é chamado se o gerenciador não puder fornecer uma resposta válida de qualquer fonte. Esse é o momento ideal para fornecer algum tipo de resposta alternativa como alternativa à falha total.

Todos esses callbacks são async e, portanto, exigem que await seja usado sempre que um evento de cache ou de busca atinja o ponto relevante para o callback em questão.

Se um plug-in usasse todos os callbacks acima, o código resultante seria este:

const myPlugin = {
  cacheWillUpdate: async ({request, response, event, state}) => {
    // Return `response`, a different `Response` object, or `null`.
    return response;
  },
  cacheDidUpdate: async ({
    cacheName,
    request,
    oldResponse,
    newResponse,
    event,
    state,
  }) => {
    // No return expected
    // Note: `newResponse.bodyUsed` is `true` when this is called,
    // meaning the body has already been read. If you need access to
    // the body of the fresh response, use a technique like:
    // const freshResponse = await caches.match(request, {cacheName});
  },
  cacheKeyWillBeUsed: async ({request, mode, params, event, state}) => {
    // `request` is the `Request` object that would otherwise be used as the cache key.
    // `mode` is either 'read' or 'write'.
    // Return either a string, or a `Request` whose `url` property will be used as the cache key.
    // Returning the original `request` will make this a no-op.
    return request;
  },
  cachedResponseWillBeUsed: async ({
    cacheName,
    request,
    matchOptions,
    cachedResponse,
    event,
    state,
  }) => {
    // Return `cachedResponse`, a different `Response` object, or null.
    return cachedResponse;
  },
  requestWillFetch: async ({request, event, state}) => {
    // Return `request` or a different `Request` object.
    return request;
  },
  fetchDidFail: async ({originalRequest, request, error, event, state}) => {
    // No return expected.
    // Note: `originalRequest` is the browser's request, `request` is the
    // request after being passed through plugins with
    // `requestWillFetch` callbacks, and `error` is the exception that caused
    // the underlying `fetch()` to fail.
  },
  fetchDidSucceed: async ({request, response, event, state}) => {
    // Return `response` to use the network response as-is,
    // or alternatively create and return a new `Response` object.
    return response;
  },
  handlerWillStart: async ({request, event, state}) => {
    // No return expected.
    // Can set initial handler state here.
  },
  handlerWillRespond: async ({request, response, event, state}) => {
    // Return `response` or a different `Response` object.
    return response;
  },
  handlerDidRespond: async ({request, response, event, state}) => {
    // No return expected.
    // Can record final response details here.
  },
  handlerDidComplete: async ({request, response, error, event, state}) => {
    // No return expected.
    // Can report any data here.
  },
  handlerDidError: async ({request, event, error, state}) => {
    // Return a `Response` to use as a fallback, or `null`.
    return fallbackResponse;
  },
};

O objeto event disponível nos callbacks listados acima é o evento original que acionou a ação de busca ou armazenamento em cache. Às vezes, não há um evento original. Por isso, seu código precisa verificar se ele existe antes de fazer referência a ele.

Todos os callbacks de plug-in também recebem um objeto state, que é exclusivo para um plug-in específico e a estratégia que ele invoca. Isso significa que você pode escrever plug-ins em que um callback pode executar condicionalmente uma tarefa com base no que outro callback no mesmo plug-in fez. Por exemplo, calcule a diferença entre executar requestWillFetch() e fetchDidSucceed() ou fetchDidFail().

Plug-ins de terceiros

Se você desenvolver um plug-in e achar que ele é útil fora do seu projeto, recomendamos publicá-lo como um módulo. Confira abaixo uma pequena lista de plug-ins do Workbox fornecidos pela comunidade:

Você pode encontrar mais plug-ins do Workbox fornecidos pela comunidade pesquisando no repositório do npm.

Por fim, se você criou um plug-in do Workbox e quer compartilhar, adicione a palavra-chave workbox-plugin ao publicá-lo. Se você tiver, entre em contato pelo Twitter @WorkboxJS.