Uso de complementos

Cuando usas Workbox, es posible que desees manipular una solicitud y una respuesta mientras se recuperan o almacenan en caché. Los complementos de Workbox te permiten agregar comportamientos adicionales a tu service worker con un mínimo de código estándar adicional. Pueden empaquetarse y reutilizarse en tus propios proyectos, o bien lanzarte públicamente para que otras personas también las usen.

Workbox proporciona una variedad de complementos listos para usar y, si prefieres hacerlo con habilidad, puedes escribir complementos personalizados que se adapten a los requisitos de tu aplicación.

Complementos de Workbox disponibles

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

Los complementos de Workbox (ya sean uno de los mencionados 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 necesita 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 correcto. La Strategy pasa la información relevante de la función de devolución de llamada sobre la solicitud o la respuesta actual, lo que le brinda a tu complemento el contexto que necesita para actuar. Se admiten las siguientes funciones de devolución de llamada:

  • cacheWillUpdate: Se llama antes de que se use Response para actualizar una caché. En este método, la respuesta se puede cambiar antes de agregarla a la caché, o puedes mostrar null para evitar actualizar la caché por completo.
  • cacheDidUpdate: Se llama cuando se agrega una nueva entrada a una caché o si se actualiza una entrada existente. Los complementos que usan este método pueden ser útiles cuando quieras realizar una acción después de una actualización de la caché.
  • cacheKeyWillBeUsed: Se llama antes de que una solicitud se use como clave de caché. Esto ocurre tanto para las búsquedas en caché (cuando mode es 'read') como para las escrituras 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 memorias caché.
  • cachedResponseWillBeUsed: Se llama a este método 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 mostrar null.
  • requestWillFetch: Se 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, probablemente debido a la ausencia de conectividad de red, y no se activa cuando el navegador tiene una conexión de red, pero recibe un error (por ejemplo, 404 Not Found).
  • fetchDidSucceed: Se llama cada vez que una solicitud de red se realiza correctamente, sin importar el código de respuesta HTTP.
  • handlerWillStart: Se llama antes de que la lógica del controlador comience a ejecutarse, lo cual es útil si necesitas establecer el estado inicial del controlador. Por ejemplo, si quieres saber cuánto tardó el controlador en generar una respuesta, puedes tomar nota de 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 RouteHandler o a otra lógica personalizada.
  • handlerDidRespond: Se llama después de que el método handle() de la estrategia muestra una respuesta. Aquí es cuando puede ser útil registrar los detalles de la respuesta final (por ejemplo, después de los cambios realizados por otros complementos).
  • handlerDidComplete: Se llama después de que se hayan resuelto todas las promesas de extensión 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 cosas como el estado de acierto de caché, latencia de caché, 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 un error absoluto.

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

Si un complemento usó 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 caché. A veces, no habrá un evento original, por lo que tu código debe verificar si existe antes de hacer referencia a él.

Todas las devoluciones de llamada del complemento también reciben un objeto state, que es exclusivo de un complemento en particular y de 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 se usa fuera de tu proyecto, te recomendamos que lo publiques como un módulo. A continuación, se muestra una lista breve de complementos de Workbox proporcionados por la comunidad:

Para encontrar más complementos de Workbox proporcionados por la comunidad, realiza búsquedas en el repositorio de npm.

Por último, si compilaste un complemento de Workbox que deseas compartir, agrega la palabra clave workbox-plugin cuando la publiques. Si es así, háganoslo saber por Twitter: @WorkboxJS.