El almacenamiento en caché es una herramienta potente. Hace que las apps dependan menos de las condiciones de red. Si usas correctamente las cachés, puedes hacer que tu app web esté disponible sin conexión y entregar tus recursos lo más rápido posible en cualquier condición de red. Como se menciona en Recursos y datos, puedes decidir la mejor estrategia para almacenar en caché los recursos necesarios. Para administrar la caché, tu service worker interactúa con la API de Cache Storage.
La API de Cache Storage está disponible en diferentes contextos:
- El contexto de la ventana (el subproceso principal de la AWP)
- El trabajador de servicio.
- Cualquier otro trabajador que uses
Una ventaja de administrar la caché con los trabajadores del servicio es que su ciclo de vida no está vinculado a la ventana, lo que significa que no estás bloqueando el subproceso principal. Ten en cuenta que, para usar la API de Cache Storage, la mayoría de estos contextos deben estar bajo una conexión TLS.
Qué almacenar en caché
La primera pregunta que podrías tener sobre el almacenamiento en caché es qué almacenar en caché. Si bien no hay una sola respuesta a esa pregunta, puedes comenzar con todos los recursos mínimos que necesitas para renderizar la interfaz de usuario.
Esos recursos deben incluir lo siguiente:
- El código HTML de la página principal (la URL de inicio de tu app)
- Hojas de estilo CSS necesarias para la interfaz de usuario principal.
- Imágenes que se usan en la interfaz de usuario.
- Son archivos JavaScript necesarios para renderizar la interfaz de usuario.
- Datos, como un archivo JSON, necesarios para renderizar una experiencia básica.
- Fuentes web
- En una aplicación de varias páginas, otros documentos HTML que deseas entregar rápidamente o sin conexión
Listo para el uso sin conexión
Si bien ser compatible con el modo sin conexión es uno de los requisitos de una app web progresiva, es fundamental comprender que no todas las AWP necesitan una experiencia sin conexión completa, por ejemplo, las soluciones de juegos en la nube o las apps de activos criptográficos. Por lo tanto, está bien ofrecer una interfaz de usuario básica que guíe a los usuarios en esas situaciones.
Tu AWP no debe renderizar un mensaje de error de un navegador que indique que el motor de renderización web no pudo cargar la página. En su lugar, usa tu trabajador de servicio para mostrar tus propios mensajes y evitar un error genérico y confuso del navegador.
Existen muchas estrategias de almacenamiento en caché diferentes que puedes usar según las necesidades de tu AWP. Por eso, es importante diseñar el uso de la caché para proporcionar una experiencia rápida y confiable. Por ejemplo, si todos los recursos de tu app se descargan rápido, no consumen mucho espacio y no necesitan actualizarse en cada solicitud, almacenar en caché todos los recursos sería una estrategia válida. Por otro lado, si tienes recursos que deben tener la versión más reciente, tal vez sea conveniente que consideres no almacenar esos recursos en caché.
Cómo usar la API
Usa la API de Cache Storage para definir un conjunto de cachés dentro de tu origen, cada una identificada con un nombre de cadena que puedes definir. Accede a la API a través del objeto caches
, y el método open
habilita la creación o apertura de una caché ya creada. El método open muestra una promesa para el objeto de caché.
caches.open("pwa-assets")
.then(cache => {
// you can download and store, delete or update resources with cache arguments
});
Descarga y almacena recursos
Para pedirle al navegador que descargue y almacene los recursos, usa los métodos add
o addAll
. El método add
realiza una solicitud y almacena una respuesta HTTP, y addAll
un grupo de respuestas HTTP como una transacción basada en un array de solicitudes o URLs.
caches.open("pwa-assets")
.then(cache => {
cache.add("styles.css"); // it stores only one resource
cache.addAll(["styles.css", "app.js"]); // it stores two resources
});
La interfaz de almacenamiento en caché almacena toda una respuesta, incluidos todos los encabezados y el cuerpo. En consecuencia, puedes recuperarlo más tarde usando una solicitud HTTP o una URL como clave. Verás cómo hacerlo en el capítulo Publicación.
Cuándo almacenar en caché
En tu AWP, eres responsable de decidir cuándo almacenar los archivos en caché. Si bien un enfoque es almacenar tantos recursos como sea posible cuando se instala el trabajador de servicio, por lo general, no es la mejor idea. La caché de recursos innecesarios desperdicia ancho de banda y espacio de almacenamiento, y podría hacer que tu app publique recursos desactualizados no deseados.
No es necesario que almacenes en caché todos los recursos a la vez. Puedes almacenar en caché recursos muchas veces durante el ciclo de vida de tu AWP, por ejemplo:
- En la instalación del service worker.
- Después de la primera carga de página.
- Cuando el usuario navega a una sección o ruta
- Cuando la red está inactiva.
Puedes solicitar almacenar archivos nuevos en caché en el subproceso principal o dentro del contexto del service worker.
Almacenamiento en caché de recursos en un service worker
Una de las situaciones más comunes es almacenar en caché un conjunto mínimo de recursos cuando se instala el trabajador del servicio. Para ello, puedes usar la interfaz de almacenamiento en caché dentro del evento install
en el trabajador de servicio.
Como el subproceso del trabajador de servicio se puede detener en cualquier momento, puedes solicitarle al navegador que espere a que finalice la promesa de addAll
para aumentar la oportunidad de almacenar todos los recursos y mantener la coherencia de la app. En el siguiente ejemplo, se muestra cómo hacerlo con el método waitUntil
del argumento del evento recibido en el objeto de escucha de eventos del trabajador de servicio.
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", event => {
event.waitUntil(
caches.open("pwa-assets")
.then(cache => {
return cache.addAll(urlsToCache);
});
);
});
El método waitUntil()
recibe una promesa y le pide al navegador que espere a que se resuelva la tarea de la promesa (completada o con errores) antes de finalizar el proceso del service worker. Es posible que debas encadenar promesas y mostrar las llamadas add()
o addAll()
para que un solo resultado llegue al método waitUntil()
.
También puedes controlar promesas con la sintaxis async/await. En ese caso, debes crear una función asíncrona que pueda llamar a await
y que devuelva una promesa a waitUntil()
después de que se la llame, como en el siguiente ejemplo:
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", (event) => {
let cacheUrls = async () => {
const cache = await caches.open("pwa-assets");
return cache.addAll(urlsToCache);
};
event.waitUntil(cacheUrls());
});
Solicitudes entre dominios y respuestas opacas
Tu AWP puede descargar y almacenar en caché recursos de tu origen y de varios dominios, como el contenido de CDN de terceros. Con una aplicación multidominio, la interacción de la caché es muy similar a las solicitudes del mismo origen. Se ejecuta la solicitud y se almacena una copia de la respuesta en la caché. Al igual que con otros elementos almacenados en caché, solo está disponible para usarse en el origen de tu app.
El recurso se almacenará como una respuesta opaca, lo que significa que tu código no podrá ver ni modificar el contenido ni los encabezados de esa respuesta. Además, las respuestas opacas no exponen su tamaño real en la API de almacenamiento, lo que afecta las cuotas. Algunos navegadores exponen tamaños grandes, como 7 MB, sin importar si el archivo es de solo 1 KB.
Actualiza y borra recursos
Puedes actualizar los recursos con cache.put(request, response)
y borrar los recursos con delete(request)
.
Consulta la documentación del objeto de caché para obtener más detalles.
Cómo depurar el almacenamiento en caché
Muchos navegadores ofrecen una forma de depurar el contenido del almacenamiento en caché en la pestaña Aplicación de DevTools. Allí, puedes ver el contenido de cada caché dentro del origen actual. Hablaremos más sobre estas herramientas en el capítulo Herramientas y depuración.