Es posible que algunos sitios web deban comunicarse con el service worker informado sobre el resultado. Estos son algunos ejemplos:
- Una página envía al service worker una lista de URL a de carga previa, de modo que, cuando el usuario haga clic en un vínculo, el documento o los subrecursos de la página ya están disponibles en la caché, lo que facilita a navegar de manera más rápida.
- La página le pide al service worker que recupere y almacene en caché un conjunto de artículos principales para disponer de ellos disponibles sin conexión.
Delegar estos tipos de tareas no críticas al service worker tiene el beneficio de liberar subproceso principal para manejar mejor las tareas más urgentes, como responder a las interacciones del usuario.
En esta guía, exploraremos cómo implementar una técnica de comunicación unidireccional desde la página para usando las APIs del navegador estándar y la biblioteca de Workbox. Estos tipos de los casos de uso, como el almacenamiento en caché imperativo.
Caso de producción
1-800-Flowers.com implementó el almacenamiento en caché imperativo (carga previa) con service workers a través de
postMessage()
para cargar previamente el
artículos principales en las páginas de categorías para acelerar la navegación posterior a las páginas de detalles de los productos.
Usan un enfoque mixto para decidir qué elementos cargar previamente:
- En el momento de cargar la página, le piden al servicer worker que recupere los datos JSON de los 9 elementos principales. agregar los objetos de respuesta resultantes a la caché.
- Para los elementos restantes, escucha el
mouseover
. de modo que, cuando un el usuario mueve el cursor sobre un elemento, puede activar la recuperación del recurso a pedido.
Usan la API de Cache para almacenar archivos JSON respuestas:
Cuando el usuario hace clic en un elemento, los datos JSON asociados a este se pueden obtener de la caché, sin necesidad de acceder a la red, lo que agiliza la navegación.
Uso de Workbox
Workbox ofrece una manera fácil de enviar mensajes a
un service worker, a través del paquete workbox-window
, un conjunto de módulos
para ejecutarse en el contexto de la ventana. Son un complemento de los otros paquetes de Workbox.
que se ejecutan en el service worker.
Para comunicar la página con el service worker, primero obtén una referencia de objeto Workbox al service worker registrado:
const wb = new Workbox('/sw.js');
wb.register();
Luego, puedes enviar el mensaje directamente de forma declarativa, sin la molestia de obtener el registro, la comprobación de la activación o el pensamiento en la API de comunicación subyacente:
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
El service worker implementa un controlador message
para
escucha estos mensajes. Opcionalmente, puede mostrar una respuesta, aunque, en casos como estos, es
no es necesario:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PREFETCH') {
// do something
}
});
Uso de las APIs del navegador
Si la biblioteca de Workbox no es suficiente para tus necesidades, aquí te mostramos cómo puedes implementar Window to Service la comunicación con los trabajadores con las APIs del navegador.
La API de postMessage se puede usar para establecer un mecanismo de comunicación unidireccional entre la página y el service worker.
La página llama
postMessage()
en
service worker:
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
payload: 'some data to perform the task',
});
El service worker implementa un controlador message
para
escucha estos mensajes.
self.addEventListener('message', (event) => {
if (event.data && event.data.type === MSG_ID) {
// do something
}
});
El atributo {type : 'MSG_ID'}
no es absolutamente obligatorio, pero es una forma de permitir que la página
enviar diferentes tipos de instrucciones al service worker (es decir, “cargar previamente” frente a “borrar”
almacenamiento'). El service worker puede ramificarse en diferentes rutas de ejecución según esta marca.
Si la operación se realizó correctamente, el usuario podrá obtener los beneficios de ella, pero, de no ser así, no alterará el flujo de usuarios principal. Por ejemplo, cuando 1-800-Flowers.com intenta almacenar en caché previamente, no es necesario que la página sepa si el service worker tuvo éxito. Si es así, el usuario disfrutará de una navegación más rápida. De lo contrario, la página aún debe navegar a la nueva página. Solo va a tardar un poco más.
Un ejemplo simple de carga previa
Una de las aplicaciones más comunes del almacenamiento en caché imperativo es la carga previa, que significa recuperar para una URL determinada, antes de que el usuario continúe con ella, con el fin de acelerar la navegación.
Existen diferentes maneras de implementar la carga previa en los sitios:
- Usa etiquetas de carga previa de vínculos en páginas: los recursos se guardan en
caché del navegador durante cinco minutos. Luego de este tiempo, se ejecutarán las reglas
Cache-Control
normales para el recurso aplicar. - Complementar la técnica anterior con una estrategia de almacenamiento en caché en el entorno de ejecución en el servicio worker para extender la vida útil de la carga previa recurso por encima de este límite.
Para situaciones de carga previa relativamente sencillas, como la carga previa de documentos o recursos específicos (JS, CSS, etc.), estas técnicas son el mejor enfoque.
Si se requiere lógica adicional, por ejemplo, analizar el recurso de carga previa (un archivo o una página JSON) en para recuperar sus URLs internas, es más apropiado delegar esta tarea por completo al service worker.
Delegar estos tipos de operaciones al service worker tiene las siguientes ventajas:
- Transferir la carga pesada del procesamiento de recuperación y posterior (lo que se presentará pronto) más adelante) a un subproceso secundario. De esta manera, se libera el subproceso principal para controlar las funciones tareas como responder a las interacciones del usuario.
- Permitir que varios clientes (por ejemplo, pestañas) reutilicen una funcionalidad común e incluso llamar al al mismo tiempo sin bloquear el subproceso principal.
Cargar previamente las páginas de detalles de los productos
Primer uso de postMessage()
en
la interfaz del service worker y pasas un array de URL a la caché:
navigator.serviceWorker.controller.postMessage({
type: 'PREFETCH',
payload: {
urls: [
'www.exmaple.com/apis/data_1.json',
'www.exmaple.com/apis/data_2.json',
],
},
});
En el service worker, implementa un controlador message
para
interceptar y procesar mensajes enviados por cualquier pestaña activa:
addEventListener('message', (event) => {
let data = event.data;
if (data && data.type === 'PREFETCH') {
let urls = data.payload.urls;
for (let i in urls) {
fetchAsync(urls[i]);
}
}
});
En el código anterior, presentamos una pequeña función auxiliar llamada fetchAsync()
para iterar en el
Array de URLs y emitir una solicitud de recuperación para cada una de ellas:
async function fetchAsync(url) {
// await response of fetch call
let prefetched = await fetch(url);
// (optionally) cache resources in the service worker storage
}
Cuando se obtiene la respuesta, puedes confiar en los encabezados de almacenamiento en caché del recurso. En muchos casos,
sin embargo, al igual que en las páginas de detalles de los productos, los recursos no se almacenan en caché (es decir, tienen un
encabezado Cache-control
de no-cache
). En casos como estos, puedes anular este comportamiento
Almacenar el recurso recuperado en la caché del service worker. Esto tiene el beneficio adicional de permitir
que se entregará sin conexión.
Más allá de los datos JSON
Una vez que se recuperan los datos JSON del extremo del servidor, a menudo contienen otras URLs que también se que vale la pena cargar previamente, como una imagen u otros datos de extremo que estén asociados con este de datos no estructurados.
Supongamos que, en nuestro ejemplo, los datos JSON devueltos son la información de un sitio de compra de alimentos:
{
"productName": "banana",
"productPic": "https://cdn.example.com/product_images/banana.jpeg",
"unitPrice": "1.99"
}
Modifica el código fetchAsync()
para iterar la lista de productos y almacenar en caché la hero image de
cada uno de ellos:
async function fetchAsync(url, postProcess) {
// await response of fetch call
let prefetched = await fetch(url);
//(optionally) cache resource in the service worker cache
// carry out the post fetch process if supplied
if (postProcess) {
await postProcess(prefetched);
}
}
async function postProcess(prefetched) {
let productJson = await prefetched.json();
if (productJson && productJson.product_pic) {
fetchAsync(productJson.product_pic);
}
}
Puedes agregar algún control de excepciones en este código para situaciones como los errores 404. Pero lo mejor de usar un service worker para la carga previa es que puede fallar sin que esto ocurra consecuencia a la página y al subproceso principal. También puedes tener una lógica más elaborada procesamiento previo del contenido cargado previamente, lo que lo hace más flexible y está separado de los datos de datos. Todo es posible.
Conclusión
En este artículo, abordamos un caso de uso común de la comunicación unidireccional entre la página y el servicio. trabajador: el almacenamiento en caché imperativo. Los ejemplos analizados solo sirven para mostrar una forma de usando este patrón y el mismo enfoque también se pueden aplicar a otros casos de uso, por ejemplo, almacenar en caché los artículos principales on demand para consumirlos sin conexión, agregarlos a favoritos y otros.
Para ver más patrones de comunicación con la página y el service worker, consulta:
- Actualizaciones de transmisión: Llama a la página desde el service worker para informar. actualizaciones importantes (p.ej., hay disponible una nueva versión de la aplicación web).
- Comunicación bidireccional: Es la delegación de una tarea a un service worker (p.ej., una descarga pesada) y mantener la página informada sobre el progreso.