Ao usar o Workbox, talvez você queira manipular uma solicitação e uma resposta enquanto ela é buscada ou armazenada em cache. Os plug-ins da caixa de trabalho permitem adicionar outros comportamentos ao service worker com o mínimo de código boilerplate. Eles podem ser empacotados e reutilizados nos seus próprios projetos ou liberados publicamente para uso por outras pessoas.
O Workbox oferece uma série de plug-ins prontos para uso que estão disponíveis para nós e, se você for do seu jeito, poderá criar plug-ins personalizados de acordo com as necessidades do seu aplicativo.
Plug-ins do Workbox disponíveis
O Workbox oferece os seguintes plug-ins oficiais para uso no service worker:
BackgroundSyncPlugin
: se uma solicitação de rede falhar, este 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 que você envie uma mensagem em um canal de transmissão ou viapostMessage()
sempre que um cache for atualizado.CacheableResponsePlugin
: armazenar em cache somente as solicitações que atendem a critérios específicos.ExpirationPlugin
: gerencia o número e a idade máxima dos itens em um cache.RangeRequestsPlugin
: responda a solicitações que incluem um cabeçalho de solicitação HTTPRange
.
Os plug-ins do Workbox, sejam eles um dos plug-ins listados acima ou personalizados, são usados com uma estratégia do Workbox por meio da adição de uma instância do plug-in à propriedade plugins
(link em inglês) 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 Strategy, as funções de callback são executadas automaticamente no momento certo. A Strategy transmite informações relevantes à função de callback sobre a solicitação e/ou resposta atual, fornecendo ao plug-in o contexto necessário para agir. Há suporte para as seguintes funções de callback:
cacheWillUpdate
: chamado antes que umResponse
seja usado para atualizar um cache. Nesse método, a resposta pode ser alterada antes de ser adicionada ao cache, ou você pode retornarnull
para evitar a atualização completa do cache.cacheDidUpdate
: chamado quando uma nova entrada é adicionada a um cache ou se uma entrada existente é atualizada. Os plug-ins que usam esse método podem ser úteis quando você quiser executar uma ação após uma atualização de cache.cacheKeyWillBeUsed
: chamado antes que uma solicitação seja usada como chave de cache. Isso ocorre nas pesquisas de cache (quandomode
é'read'
) e nas gravações em cache (quandomode
é'write'
). Esse callback é útil se você precisa modificar 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, é possível retornar uma resposta diferente ou retornarnull
.requestWillFetch
: chamado sempre que uma solicitação está prestes a ir para a rede. Útil quando você precisar mudar oRequest
logo antes de ir para a rede.fetchDidFail
: chamado quando uma solicitação de rede falha, provavelmente devido à ausência de conectividade de rede, e não é disparado quando o navegador tem uma conexão de rede, mas recebe um erro (por exemplo,404 Not Found
).fetchDidSucceed
: chamado sempre que uma solicitação de rede é bem-sucedida, independentemente do código de resposta HTTP.handlerWillStart
: chamado antes de qualquer lógica do gerenciador começar a ser executada, o que é ú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 que o métodohandle()
da estratégia retorne uma resposta, o que é útil se você precisar modificar uma resposta antes de retorná-la para umaRouteHandler
ou outra lógica personalizada.handlerDidRespond
: chamado depois que o métodohandle()
da estratégia retorna uma resposta. Nesse momento, pode ser útil registrar os detalhes finais da resposta (por exemplo, depois de alterações feitas por outros plug-ins).handlerDidComplete
: chamado depois que todas as promessas de ciclo de vida adicionadas ao evento a partir da invocação da estratégia forem resolvidas. Isso é útil se você precisar gerar relatórios sobre dados que precisem esperar até que o gerenciador seja concluído para calcular itens 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 origem. Esse é o momento ideal para fornecer algum tipo de resposta substituta como alternativa à falha imediata.
Todos esses callbacks são async
e, portanto, vão exigir que o await
seja usado sempre que um evento de cache ou de busca atingir o ponto relevante para o callback em questão.
Se um plug-in usasse todos os callbacks acima, este seria o código resultante:
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 vai haver um evento original. Por isso, seu código precisa verificar se ele existe antes de fazer referência a ele.
Todos os callbacks do plug-in também recebem um objeto state
, que é exclusivo de determinado plug-in e da estratégia que ele invoca. Isso significa que é possível criar plug-ins em que um callback pode executar uma tarefa condicionalmente com base no que outro callback do mesmo plug-in fez (por exemplo, calcular a diferença entre executar requestWillFetch()
e fetchDidSucceed()
ou fetchDidFail()
).
Plug-ins de terceiros
Se você desenvolve um plug-in e acha que ele pode ser usado fora do seu projeto, recomendamos que ele seja publicado como um módulo. Confira abaixo uma pequena lista de plug-ins do Workbox fornecidos pela comunidade:
cloudinary-workbox-plugin
, que reescreve dinamicamente solicitações de imagens hospedadas no Cloudinary com base na velocidade de conexão atual.- O
workbox-plugin-firebase-auth
ajuda a gerenciar oAuthorization: Bearer
para solicitações enviadas que precisam da autenticação do Firebase.
Pesquise no repositório do npm para encontrar mais plug-ins do Workbox fornecidos pela comunidade.
Por fim, se você criou um plug-in do Workbox que gostaria de compartilhar, adicione a palavra-chave workbox-plugin
ao publicá-lo. Se tiver, mande para nós pelo Twitter @WorkboxJS.