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

Cómo permitir que service worker les indique a los navegadores qué páginas funcionan sin conexión

¿Qué es la API de indexación de contenido?

Usar una app web progresiva implica tener acceso a la información que les interesa a las personas (imágenes, videos, artículos y mucho más), sin importar el estado actual de la conexión de red. Las tecnologías como los service Workers, la API de Cache Storage y IndexedDB te proporcionan los componentes 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 prioriza el uso sin conexión es solo una parte del proceso. Si los usuarios no se dan cuenta de que el contenido de una app web está disponible mientras están sin conexión, no aprovecharán al máximo el trabajo que hiciste para implementar esa funcionalidad.

Este es un problema de descubrimiento. ¿Cómo puede tu AWP dar a conocer a los usuarios su contenido disponible sin conexión para que puedan descubrir y ver lo que está disponible? La API de Content Indexing es la solución a este problema. La parte de esta solución orientada al desarrollador es una extensión de los service workers, que permite a los desarrolladores agregar URLs y metadatos de páginas que pueden funcionar sin conexión a un índice local que mantiene el navegador. Esta mejora está disponible en Chrome 84 y versiones posteriores.

Una vez que el índice se complete con contenido de tu AWP, así como con otras AWP instaladas, el navegador mostrará el índice 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 hayan agregado 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 no tiene conexión.

La API de Content Indexing no es una forma alternativa de almacenar contenido en caché. Es una forma de proporcionar metadatos sobre páginas que tu service worker ya almacena en caché, de modo que el navegador pueda mostrar esas páginas cuando es probable que los usuarios quieran verlas. La API de Content Indexing ayuda con la descubrimiento de las páginas almacenadas en caché.

Observa cómo funciona

La mejor manera de conocer la API de Content Indexing es probar una aplicación de ejemplo.

  1. Asegúrate de usar un navegador y una plataforma compatibles. Actualmente, eso se limita a Chrome 84 o versiones posteriores en Android. Ve a about://version para ver qué versión de Chrome estás ejecutando.
  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 de datos móviles y Wi-Fi del dispositivo, o habilita el modo de avión para simular que el navegador está sin 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 la fuente de la aplicación de ejemplo en GitHub.

Otra aplicación de ejemplo, una AWP de Scrum, 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 elementos almacenados por una app web que usa la API de Cache Storage.

Cómo usar la API

Para usar la API, tu app debe tener un service worker y URLs que se puedan navegar sin conexión. Si tu app web no tiene un service worker actualmente, las bibliotecas de Workbox pueden simplificar la creación de uno.

¿Qué tipo de URLs se pueden indexar como aptas para el uso sin conexión?

La API admite la indexación de URLs que corresponden a documentos HTML. Una URL para un archivo multimedia almacenado en caché, por ejemplo, no se puede indexar directamente. En su lugar, debes proporcionar una URL para 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 parámetro de consulta y, luego, mostrar el contenido del archivo con controles adicionales o contenido en la página.

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

Descripción general

La API de Content Indexing admite tres operaciones: agregar, enumerar y quitar metadatos. Estos métodos se exponen desde una nueva propiedad, 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 consultar el ServiceWorkerRegistration directamente a través de registration. Ya se definirá como parte de ServiceWorkerGlobalScope..

Agregando 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. Quizás desees agregar al índice como 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é con 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 window si tus íconos se basan en un controlador fetch.

Cuando llames a add(), Chrome realizará una solicitud a la URL de cada ícono para asegurarse de tener una copia del ícono para 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() en tu service worker (quizás dentro de otro controlador de eventos), la solicitud no activará el controlador fetch del service worker. Los íconos se recuperarán directamente, sin la participación de ningún service worker. Ten esto en cuenta si los íconos se basan en 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 window.

Enumera el contenido del índice

El método getAll() muestra una promesa para una lista iterable de entradas indexadas y sus metadatos. Las entradas que se muestran 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 deseas quitar:

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

Llamar a delete() solo afecta el í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 contenido indexado previamente. Así se ve la interfaz de eliminación en Chrome 80:

El elemento del menú para borrar.

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

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 del índice correspondiente.

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 acerca de la API que es incómodo o que no funciona como se espera? ¿O faltan piezas que necesitas para implementar tu idea?

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

¿Tienes problemas con la implementación?

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

Informa un error en https://new.crbug.com. Incluye todos los detalles que puedas, instrucciones simples para reproducir el contenido y establece los Componentes en Blink>ContentIndexing.

¿Piensas usar la API?

¿Piensas usar la API de Content Indexing en tu app web? Tu asistencia pública ayuda a Chrome a priorizar funciones y les muestra a otros proveedores de navegadores la importancia de admitirlas.

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

Consulta las respuestas proporcionadas en la respuesta al cuestionario de seguridad y privacidad de W3C. Si tienes más preguntas, comienza un debate en el repositorio de GitHub del proyecto.

Hero image de Maksym Kaharlytskyi en Unsplash.