Uso de complementos

Cuando usas Workbox, es posible que desees manipular una solicitud y una respuesta a medida que se recuperan o almacenan en caché. Los complementos de Workbox te permiten agregar comportamientos adicionales a tu service worker con un mínimo de texto de plantilla adicional. Se pueden empaquetar y reutilizar en tus propios proyectos, o lanzarse de forma pública para que otros también los usen.

Workbox ofrece varios complementos listos para usar y, si te gusta más, puedes escribir complementos personalizados que se adapten a las necesidades de tu aplicación.

Complementos de Workbox disponibles

Workbox ofrece los siguientes complementos oficiales para usar en tu service worker:

Los complementos de Workbox, ya sean uno de los que se mencionaron anteriormente o uno personalizado, se usan con una estrategia de Workbox. Para ello, se agrega una instancia del complemento a la propiedad plugins de la estrategia:

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 complementos personalizados

Un complemento de Workbox debe implementar una o más funciones de devolución de llamada. Cuando agregas un complemento a una estrategia, las funciones de devolución de llamada se ejecutan automáticamente en el momento adecuado. La estrategia pasa a tu función de devolución de llamada información relevante sobre la solicitud o respuesta actual, lo que le brinda a tu complemento el contexto que necesita para tomar medidas. Se admiten las siguientes funciones de devolución de llamada:

  • cacheWillUpdate: Se llama antes de que se use un Response para actualizar una caché. En este método, la respuesta se puede cambiar antes de que se agregue a la caché, o bien puedes mostrar null para evitar actualizar la caché por completo.
  • cacheDidUpdate: Se lo llama cuando se agrega una entrada nueva a una caché o se actualiza una entrada existente. Los complementos que usan este método pueden ser útiles cuando deseas realizar una acción después de una actualización de la caché.
  • cacheKeyWillBeUsed: Se llama antes de que se use una solicitud como clave de caché. Esto ocurre para las búsquedas en caché (cuando mode es 'read') y las operaciones de escritura en caché (cuando mode es 'write'). Esta devolución de llamada es útil si necesitas anular o normalizar las URLs antes de usarlas para acceder a las cachés.
  • cachedResponseWillBeUsed: Se llama justo antes de que se use una respuesta de una caché, lo que te permite examinar esa respuesta. En este momento, puedes mostrar una respuesta diferente o null.
  • requestWillFetch: Se lo llama cada vez que una solicitud está a punto de ir a la red. Es útil cuando necesitas cambiar el Request justo antes de que se envíe a la red.
  • fetchDidFail: Se llama cuando falla una solicitud de red, lo más probable es que debido a la ausencia de conectividad de red, y no se activará cuando el navegador tenga una conexión de red, pero reciba un error (por ejemplo, 404 Not Found).
  • fetchDidSucceed: Se llama cada vez que una solicitud de red se realiza correctamente, independientemente del código de respuesta HTTP.
  • handlerWillStart: Se llama antes de que comience a ejecutarse cualquier lógica del controlador, lo que es útil si necesitas establecer el estado inicial del controlador. Por ejemplo, si deseas saber cuánto tiempo tardó el controlador en generar una respuesta, puedes anotar la hora de inicio en esta devolución de llamada.
  • handlerWillRespond: Se llama antes de que el método handle() de la estrategia muestre una respuesta, lo que es útil si necesitas modificar una respuesta antes de devolverla a un RouteHandler o a otra lógica personalizada.
  • handlerDidRespond: Se llama después de que el método handle() de la estrategia muestra una respuesta. En este caso, puede ser útil registrar los detalles de la respuesta final (por ejemplo, después de los cambios que realicen otros complementos).
  • handlerDidComplete: Se llama después de que se liquidan todas las promesas de extender el ciclo de vida agregadas al evento desde la invocación de la estrategia. Esto es útil si necesitas informar sobre cualquier dato que deba esperar hasta que el controlador esté listo para calcular cuestiones como el estado de acierto de caché, la latencia de caché, la latencia de red y otra información útil.
  • handlerDidError: Se llama si el controlador no puede proporcionar una respuesta válida de ninguna fuente, que es el momento óptimo para proporcionar algún tipo de respuesta de resguardo como alternativa a fallar por completo.

Todas estas devoluciones de llamada son async y, por lo tanto, requerirán que se use await cada vez que un evento de caché o recuperación llegue al punto relevante para la devolución de llamada en cuestión.

Si un complemento usara todas las devoluciones de llamada anteriores, este sería el 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;
  },
};

El objeto event disponible en las devoluciones de llamada mencionadas anteriormente es el evento original que activó la acción de recuperación o almacenamiento en caché. A veces, no habrá un evento original, por lo que tu código debe verificar si existe antes de hacer referencia a él.

A todas las devoluciones de llamada de complementos también se les pasa un objeto state, que es único para un complemento en particular y la estrategia que invoca. Esto significa que puedes escribir complementos en los que una devolución de llamada pueda realizar una tarea de forma condicional en función de lo que hizo otra devolución de llamada en el mismo complemento (por ejemplo, calcular la diferencia entre ejecutar requestWillFetch() y fetchDidSucceed() o fetchDidFail()).

Complementos de terceros

Si desarrollas un complemento y crees que puede usarse fuera de tu proyecto, te recomendamos que lo publiques como un módulo. A continuación, se muestra una breve lista de los complementos de Workbox proporcionados por la comunidad:

Es posible que puedas encontrar más complementos de Workbox proporcionados por la comunidad si buscas en el repositorio de npm.

Por último, si compilaste un complemento de Workbox que te gustaría compartir, agrega la palabra clave workbox-plugin cuando lo publiques. Si es así, avísanos en Twitter @WorkboxJS.