Crea experiencias de búsqueda resilientes con Workbox

En este codelab, se muestra cómo implementar una experiencia de búsqueda resiliente con Workbox. La app de demostración que usa contiene un cuadro de búsqueda que llama a un extremo del servidor y redirecciona al usuario a una página HTML básica.

Medir

Antes de agregar optimizaciones, siempre es recomendable analizar el estado actual de la aplicación.

  • Haz clic en Remix to Edit para que el proyecto se pueda editar.
  • Para obtener una vista previa del sitio, presiona Ver app y, luego, Pantalla completa pantalla completa.

En la pestaña nueva que se acaba de abrir, verifica cómo se comporta el sitio web cuando se desconecta:

  1. Presiona "Control + Mayúsculas + J" (o "Comando + Opción + J" en Mac) para abrir DevTools.
  2. Haga clic en la pestaña Red.
  3. Abre las Herramientas para desarrolladores de Chrome y selecciona el panel Network.
  4. En la lista desplegable de limitación, selecciona Sin conexión.
  5. En la app de demostración, ingresa una búsqueda y, luego, haz clic en el botón Buscar.

Se muestra la página de error estándar del navegador:

Captura de pantalla de la UX sin conexión predeterminada en el navegador.

Proporciona una respuesta alternativa

El trabajador de servicio contiene el código para agregar la página sin conexión a la lista de almacenamiento previo en caché, de modo que siempre se pueda almacenar en caché en el evento install del trabajador de servicio.

Por lo general, deberás indicarle a Workbox que agregue este archivo a la lista de almacenamiento previo en caché en el momento de la compilación, integrando la biblioteca con la herramienta de compilación que elijas (p.ej., webpack o gulp).

Para simplificar, ya lo hicimos por ti. El siguiente código en public/sw.js hace eso:

const FALLBACK_HTML_URL = '/index_offline.html';

workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);

A continuación, agrega código para usar la página sin conexión como respuesta alternativa:

  1. Para ver la fuente, presiona Ver fuente.
  2. Agrega el siguiente código al final de public/sw.js:
workbox.routing.setDefaultHandler(new workbox.strategies.NetworkOnly());

workbox.routing.setCatchHandler(({event}) => {
  switch (event.request.destination) {
    case 'document':
      return caches.match(FALLBACK_HTML_URL);
      break;
    default:
      return Response.error();
  }
});

El código hace lo siguiente:

  • Define una estrategia Solo para la red predeterminada que se aplicará a todas las solicitudes.
  • Declara un controlador de errores global llamando a workbox.routing.setCatchHandler() para administrar las solicitudes fallidas. Cuando las solicitudes son para documentos, se mostrará una página HTML sin conexión de resguardo.

Para probar esta función, haz lo siguiente:

  1. Regresa a la otra pestaña en la que se ejecuta tu app.
  2. Vuelve a establecer la lista desplegable Limitación en En línea.
  3. Presiona el botón Atrás de Chrome para volver a la página de búsqueda.
  4. Asegúrate de que la casilla de verificación Inhabilitar caché en Herramientas para desarrolladores esté inhabilitada.
  5. Mantén presionado el botón Volver a cargar de Chrome y selecciona Vaciar caché y volver a cargar para asegurarte de que se actualice tu service worker.
  6. Vuelve a establecer la lista desplegable Limitación en Sin conexión.
  7. Ingresa una búsqueda y vuelve a hacer clic en el botón Buscar.

Se muestra la página HTML de resguardo:

Captura de pantalla de la UX sin conexión personalizada en el navegador.

Cómo solicitar permisos de notificaciones

Para mayor simplicidad, la página sin conexión en views/index_offline.html ya contiene el código para solicitar permisos de notificación en un bloque de secuencia de comandos en la parte inferior:

function requestNotificationPermission(event) {
  event.preventDefault();

  Notification.requestPermission().then(function (result) {
    showOfflineText(result);
  });
}

El código hace lo siguiente:

  • Cuando el usuario hace clic en Suscribirse a las notificaciones, se llama a la función requestNotificationPermission(), que llama a Notification.requestPermission() para mostrar el mensaje de permiso predeterminado del navegador. La promesa se resuelve con el permiso que elige el usuario, que puede ser granted, denied o default.
  • Pasa el permiso resuelto a showOfflineText() para mostrar el texto adecuado al usuario.

Persistir las búsquedas sin conexión y volver a intentarlo cuando se recupere la conexión

A continuación, implementa Workbox Background Sync para conservar las búsquedas sin conexión, de modo que se puedan volver a intentar cuando el navegador detecte que se restableció la conectividad.

  1. Abre public/sw.js para editarlo.
  2. Agrega el siguiente código al final del archivo:
const bgSyncPlugin = new workbox.backgroundSync.Plugin('offlineQueryQueue', {
  maxRetentionTime: 60,
  onSync: async ({queue}) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        const response = await fetch(entry.request);
        const cache = await caches.open('offline-search-responses');
        const offlineUrl = `${entry.request.url}&notification=true`;
        cache.put(offlineUrl, response);
        showNotification(offlineUrl);
      } catch (error) {
        await this.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

El código hace lo siguiente:

  • workbox.backgroundSync.Plugin contiene la lógica para agregar solicitudes fallidas a una cola, de modo que se puedan volver a intentar más tarde. Estas solicitudes se conservarán en IndexedDB.
  • maxRetentionTime indica la cantidad de tiempo durante la que se puede volver a intentar una solicitud. En este caso, elegimos 60 minutos (después de los cuales se descartará).
  • onSync es la parte más importante de este código. Se llamará a esta devolución de llamada cuando se restablezca la conexión para que se recuperen las solicitudes en cola y, luego, se recuperen de la red.
  • La respuesta de red se agrega a la caché de offline-search-responses, y se agrega el parámetro de consulta &notification=true, de modo que esta entrada de caché se pueda recuperar cuando un usuario haga clic en la notificación.

Para integrar la sincronización en segundo plano con tu servicio, define una estrategia NetworkOnly para las solicitudes a la URL de búsqueda (/search_action) y pasa el bgSyncPlugin definido anteriormente. Agrega el siguiente código al final de public/sw.js:

const matchSearchUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return url.pathname === '/search_action' && !(notificationParam === 'true');
};

workbox.routing.registerRoute(
  matchSearchUrl,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
);

Esto le indica a Workbox que siempre vaya a la red y, cuando las solicitudes fallen, use la lógica de sincronización en segundo plano.

A continuación, agrega el siguiente código al final de public/sw.js para definir una estrategia de almacenamiento en caché para las solicitudes provenientes de notificaciones. Usa una estrategia CacheFirst para que se puedan entregar desde la caché.

const matchNotificationUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return (url.pathname === '/search_action' && (notificationParam === 'true'));
};

workbox.routing.registerRoute(matchNotificationUrl,
  new workbox.strategies.CacheFirst({
     cacheName: 'offline-search-responses',
  })
);

Por último, agrega el código para mostrar notificaciones:

function showNotification(notificationUrl) {
  if (Notification.permission) {
     self.registration.showNotification('Your search is ready!', {
        body: 'Click to see you search result',
        icon: '/img/workbox.jpg',
        data: {
           url: notificationUrl
        }
     });
  }
}

self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  event.waitUntil(
     clients.openWindow(event.notification.data.url)
  );
});

Prueba la función

  1. Regresa a la otra pestaña en la que se ejecuta tu app.
  2. Vuelve a establecer la lista desplegable Limitación en En línea.
  3. Presiona el botón Atrás de Chrome para volver a la página de búsqueda.
  4. Mantén presionado el botón Volver a cargar de Chrome y selecciona Vaciar caché y volver a cargar para asegurarte de que se actualice tu service worker.
  5. Vuelve a establecer la lista desplegable Limitación en Sin conexión.
  6. Ingresa una búsqueda y vuelve a hacer clic en el botón Buscar.
  7. Haz clic en Suscribirse a las notificaciones.
  8. Cuando Chrome te pregunte si quieres otorgar permiso a la app para enviar notificaciones, haz clic en Permitir.
  9. Ingresa otra búsqueda y vuelve a hacer clic en el botón Buscar.
  10. Vuelve a establecer la lista desplegable Limitación en En línea.

Una vez que se restablezca la conexión, se mostrará una notificación:

Captura de pantalla del flujo sin conexión completo.

Conclusión

Workbox proporciona muchas funciones integradas para que tus AWP sean más resistentes y atractivas. En este codelab, exploraste cómo implementar la API de Background Sync a través de la abstracción de Workbox para garantizar que no se pierdan las búsquedas de los usuarios sin conexión y que se puedan volver a intentar una vez que se restablezca la conexión. La demostración es una app de búsqueda simple, pero puedes usar una implementación similar para situaciones y casos de uso más complejos, como apps de chat, publicación de mensajes en una red social, etcétera.