Cómo indexar páginas que pueden funcionar sin conexión con la API de Content Indexing

Permitir que los service workers indiquen a los navegadores qué páginas funcionan sin conexión

¿Qué es la API de Content Indexing?

Usar una app web progresiva significa tener acceso a información que les interesa a las personas (imágenes, videos, artículos y mucho más), independientemente del estado actual de tu conexión de red. Tecnologías como los trabajadores en segundo plano, la API de almacenamiento en caché y IndexedDB te proporcionan los elementos básicos para almacenar y entregar datos cuando las personas interactúan directamente con una AWP. Sin embargo, compilar una AWP de alta calidad que priorice el uso sin conexión es solo una parte de la historia. Si los usuarios no se dan cuenta de que el contenido de una app web está disponible sin conexión, no aprovecharán al máximo el trabajo que dedicas a implementar esa funcionalidad.

Este es un problema de descubrimiento. ¿Cómo puede tu AWP informar a los usuarios sobre su contenido sin conexión para que puedan descubrir y ver lo que está disponible? La API de Content Indexing es una solución a este problema. La parte de esta solución para desarrolladores es una extensión de los service workers, que les permite agregar las URLs y los metadatos de las páginas compatibles con el uso sin conexión a un índice local que mantiene el navegador. Esa mejora está disponible en Chrome 84 y versiones posteriores.

Una vez que el índice se complete con contenido de tu AWP, así como de cualquier otra AWP instalada, el navegador lo mostrará como se muestra a continuación.

Captura de pantalla del elemento de menú Descargas en la página Nueva pestaña de Chrome.
Primero, selecciona el elemento de menú Descargas en la página Nueva pestaña de Chrome.
Contenido multimedia y artículos que se agregaron al índice
El contenido multimedia y los artículos que se agregaron al índice se mostrarán en la sección Artículos para ti.

Además, Chrome puede recomendar contenido de forma proactiva cuando detecta que un usuario está sin conexión.

La API de Content Indexing no es una alternativa para almacenar contenido en caché. Es una forma de proporcionar metadatos sobre las páginas que ya están almacenadas en caché por el trabajador de tu servicio, de modo que el navegador pueda mostrar esas páginas cuando sea probable que los usuarios quieran verlas. La API de Content Indexing ayuda con la visibilidad de las páginas almacenadas en caché.

Observa cómo funciona

La mejor manera de familiarizarse con la API de Content Indexing es probar una aplicación de muestra.

  1. Asegúrate de usar un navegador y una plataforma compatibles. Por el momento, se limita a Chrome 84 o versiones posteriores en Android. Ve a about://version para ver qué versión de Chrome ejecutas.
  2. Visita https://contentindex.dev.
  3. Haz clic en el botón + junto a uno o más de los elementos de la lista.
  4. (Opcional) Inhabilita la conexión Wi-Fi y de datos móviles del dispositivo, o habilita el modo de avión para simular que el navegador no tiene conexión.
  5. Elige Descargas en el menú de Chrome y cambia a la pestaña Artículos para ti.
  6. Explora el contenido que guardaste anteriormente.

Puedes ver el código fuente de la aplicación de ejemplo en GitHub.

Otra aplicación de ejemplo, una PWA de Scrapbook, ilustra el uso de la API de Content Indexing con la API de Web Share Target. En el código, se muestra una técnica para mantener la API de Content Indexing sincronizada con los elementos que almacena una app web con la API de Cache Storage.

Cómo usar la API

Para usar la API, tu app debe tener un trabajador de servicio y URLs que se puedan navegar sin conexión. Si tu app web no tiene un trabajador de servicio en este momento, las bibliotecas de Workbox pueden simplificar su creación.

¿Qué tipo de URLs se pueden indexar como compatibles con la conexión sin conexión?

La API admite la indexación de URLs correspondientes a documentos HTML. Por ejemplo, una URL de un archivo multimedia almacenado en caché no se puede indexar directamente. En su lugar, debes proporcionar una URL de una página que muestre contenido multimedia y que funcione sin conexión.

Un patrón recomendado es crear una página HTML de "visualizador" que pueda aceptar la URL de contenido multimedia subyacente como un parámetro de consulta y, luego, mostrar el contenido del archivo, posiblemente con controles o contenido adicionales en la página.

Las apps web solo pueden agregar URLs al índice de contenido que están dentro del alcance del service worker actual. En otras palabras, una app web no puede agregar al índice de contenido una URL que pertenezca a un dominio completamente diferente.

Descripción general

La API de Content Indexing admite tres operaciones: agregar, mostrar y quitar metadatos. Estos métodos se exponen desde una propiedad nueva, index, que se agregó a la interfaz ServiceWorkerRegistration.

El primer paso para indexar contenido es obtener una referencia al ServiceWorkerRegistration actual. Usar navigator.serviceWorker.ready es la manera más directa:

const registration = await navigator.serviceWorker.ready;

// Remember to feature-detect before using the API:
if ('index' in registration) {
  // Your Content Indexing API code goes here!
}

Si realizas llamadas a la API de Content Indexing desde un service worker, en lugar de hacerlo desde una página web, puedes hacer referencia a ServiceWorkerRegistration directamente a través de registration. Ya estará definido como parte de ServiceWorkerGlobalScope..

Cómo agregar al índice

Usa el método add() para indexar las URLs y sus metadatos asociados. Puedes elegir cuándo se agregan los elementos al índice. Es posible que desees agregar el texto al índice en respuesta a una entrada, como hacer clic en el botón “Guardar sin conexión”. También puedes agregar elementos automáticamente cada vez que se actualicen los datos almacenados en caché a través de un mecanismo como la sincronización periódica en segundo plano.

await registration.index.add({
  // Required; set to something unique within your web app.
  id: 'article-123',

  // Required; url needs to be an offline-capable HTML page.
  url: '/articles/123',

  // Required; used in user-visible lists of content.
  title: 'Article title',

  // Required; used in user-visible lists of content.
  description: 'Amazing article about things!',

  // Required; used in user-visible lists of content.
  icons: [{
    src: '/img/article-123.png',
    sizes: '64x64',
    type: 'image/png',
  }],

  // Optional; valid categories are currently:
  // 'homepage', 'article', 'video', 'audio', or '' (default).
  category: 'article',
});

Agregar una entrada solo afecta el índice de contenido; no agrega nada a la caché.

Caso extremo: Llama a add() desde el contexto de window si tus íconos dependen de un controlador fetch.

Cuando llames a add(), Chrome solicitará la URL de cada ícono para asegurarse de que tenga una copia que se pueda usar cuando se muestre una lista de contenido indexado.

  • Si llamas a add() desde el contexto window (en otras palabras, desde tu página web), esta solicitud activará un evento fetch en tu service worker.

  • Si llamas a add() dentro de tu trabajador de servicio (quizás dentro de otro controlador de eventos), la solicitud no activará el controlador fetch del trabajador de servicio. Los íconos se recuperarán directamente, sin la participación de ningún trabajador de servicio. Ten esto en cuenta si tus íconos dependen de tu controlador fetch, quizás porque solo existen en la caché local y no en la red. Si es así, asegúrate de llamar a add() solo desde el contexto de window.

Obtén una lista del contenido del índice

El método getAll() muestra una promesa para una lista iterable de entradas indexadas y sus metadatos. Las entradas que se devuelvan contendrán todos los datos guardados con add().

const entries = await registration.index.getAll();
for (const entry of entries) {
  // entry.id, entry.launchUrl, etc. are all exposed.
}

Cómo quitar elementos del índice

Para quitar un elemento del índice, llama a delete() con el id del elemento que quieres quitar:

await registration.index.delete('article-123');

Llamar a delete() solo afecta al índice. No borra nada de la caché.

Cómo controlar un evento de eliminación de usuario

Cuando el navegador muestra el contenido indexado, puede incluir su propia interfaz de usuario con un elemento de menú Borrar, lo que da a las personas la oportunidad de indicar que terminaron de ver el contenido indexado previamente. Así se ve la interfaz de eliminación en Chrome 80:

El elemento del menú Borrar.

Cuando alguien seleccione ese elemento de menú, el trabajador del servicio de tu app web recibirá un evento contentdelete. Si bien el control de este evento es opcional, proporciona la posibilidad de que tu service worker "limpie" contenido, como los archivos multimedia almacenados en caché local, que alguien indicó que terminó de usar.

No es necesario que llames a registration.index.delete() dentro de tu controlador contentdelete. Si se activó el evento, el navegador ya realizó la eliminación de índice relevante.

self.addEventListener('contentdelete', (event) => {
  // event.id will correspond to the id value used
  // when the indexed content was added.
  // Use that value to determine what content, if any,
  // to delete from wherever your app stores it—usually
  // the Cache Storage API or perhaps IndexedDB.
});

Comentarios sobre el diseño de la API

¿Hay algo en la API que sea incómodo o no funcione como se espera? ¿O ¿faltan piezas que necesitas para implementar tu idea?

Informa un problema en el repositorio de GitHub de la explicación de Content Indexing API o agrega tus comentarios a un problema existente.

¿Tienes problemas con la implementación?

¿Encontraste un error en la implementación de Chrome?

Envía un informe de errores a https://new.crbug.com. Incluye la mayor cantidad de detalles posible, instrucciones simples para reproducirlo y establece Componentes en Blink>ContentIndexing.

¿Tienes pensado usar la API?

¿Planeas usar la API de Content Indexing en tu app web? Tu apoyo público ayuda a Chrome a priorizar las funciones y les muestra a otros proveedores de navegadores lo importante que es admitirlas.

¿Cuáles son algunas de las consecuencias de seguridad y privacidad de la indexación de contenido?

Consulta las respuestas proporcionadas en respuesta al cuestionario de seguridad y privacidad del W3C. Si tienes más preguntas, inicia una conversación a través del repositorio de GitHub del proyecto.

Imagen hero de Maksym Kaharlytskyi en Unsplash.