Crea secuencias de comandos del service worker

Este codelab es parte del curso de capacitación Desarrollo de apps web progresivas, desarrollado por el equipo de capacitación de Google Developers. Aprovecharás al máximo este curso si trabajas con los codelabs en secuencia.

Para obtener información completa sobre el curso, consulta la Descripción general del desarrollo de aplicaciones web progresivas.

Introducción

En este lab, se explica cómo crear un service worker simple y se explica el ciclo de vida del service worker.

Qué aprenderás

  • Crea una secuencia de comandos de service worker básica, instálala y realiza una depuración simple

Lo que debe saber

  • Aspectos básicos de JavaScript y HTML
  • Conceptos y sintaxis básica de las promesas de ES2015
  • Cómo habilitar Play Console

Qué necesitas antes de comenzar

Descargue o clone el repositorio pwa-training-labs de GitHub y, luego, instale la versión LTS de Node.js, si es necesario.

Navega al directorio service-worker-lab/app/ y, luego, inicia un servidor de desarrollo local:

cd service-worker-lab/app
npm install
node server.js

Puedes finalizar el servidor en cualquier momento con Ctrl-c.

Abre el navegador y navega hasta localhost:8081/.

Nota: Anule el registro de cualquier service worker y borre todas las caché del service worker para localhost de modo que no interfieran en el lab. En Chrome DevTools, puede hacer clic en Borrar datos del sitio desde la sección Liberar espacio de almacenamiento de la pestaña Aplicación.

Abre la carpeta service-worker-lab/app/ en tu editor de texto preferido. En la carpeta app/, compilará el lab.

Esta carpeta contiene lo siguiente:

  • below/another.html, js/another.js, js/other.js y other.html son recursos de muestra que usamos para experimentar con el alcance del service worker
  • La carpeta styles/ contiene las hojas de estilo en cascada para este lab
  • La carpeta test/ contiene archivos para probar el progreso
  • index.html es la página HTML principal de nuestro sitio o aplicación de muestra
  • service-worker.js es el archivo JavaScript que se usa para crear nuestro service worker.
  • package.json y package-lock.json hacen un seguimiento de los paquetes de nodos que se usan en este proyecto
  • server.js es un servidor Express simple que usamos para alojar nuestra app.

Abre service-worker.js en tu editor de texto. Ten en cuenta que el archivo está vacío. Todavía no agregamos ningún código para ejecutar dentro del service worker.

Abre index.html en tu editor de texto.

Dentro de las etiquetas <script>, agrega el siguiente código para registrar el service worker:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('Service Worker is registered', registration);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

Guarde la secuencia de comandos y actualice la página. La consola debería mostrar un mensaje que indique que se registró el service worker. En Chrome, puedes verificar que un service worker esté registrado. Para ello, abre DevTools (Control + Mayúsculas + I en Windows y Linux, o ⌘ + alt + I en Mac), haz clic en la pestaña Aplicación y, luego, en la opción Service workers. Debería ver un resultado similar al siguiente:

Opcional: Abre el sitio en un navegador no compatible y verifica que funcione el condicional de la verificación de compatibilidad.

Explicación

El código anterior registra el archivo service-worker.js como service worker. Primero, se verifica si el navegador admite service workers. Debes hacerlo cada vez que registres un service worker porque es posible que algunos navegadores no admitan este tipo de service worker. El código luego registra el service worker con el método register de la API de ServiceWorkerContainer, que se encuentra en la interfaz Navigator de la ventana.

navigator.serviceWorker.register(...) muestra una promesa que se resuelve con un objeto registration una vez que se registra correctamente el service worker. Si falla el registro, se rechazará la promesa.

Los cambios en el estado del service worker activan eventos en el service worker.

Agrega objetos de escucha de eventos

Abre service-worker.js en tu editor de texto.

Agrega los siguientes objetos de escucha de eventos al service worker:

self.addEventListener('install', event => {
  console.log('Service worker installing...');
  // Add a call to skipWaiting here
});

self.addEventListener('activate', event => {
  console.log('Service worker activating...');
});

Guarda el archivo.

Cancela el registro del service worker manualmente y actualiza la página para instalarlo y activarlo. El registro de la consola debería indicar que se registró, instaló y activó el nuevo service worker.

Nota: Es posible que el registro de registro parezca desordenado con los demás registros (instalación y activación). El service worker se ejecuta en simultáneo con la página, de manera que no podemos garantizar el orden de los registros (el registro de registro proviene de la página, mientras que los registros de instalación y activación provienen del service worker). Sin embargo, los eventos de instalación, activación y otros procesos de trabajo de servicio se realizan en un orden definido dentro del service worker y siempre deben aparecer en el orden esperado.

Explicación

El service worker emite un evento install al final del registro. En el código anterior, se registra un mensaje dentro del objeto de escucha de eventos install, pero en una app real sería un buen lugar para almacenar elementos estáticos en caché.

Cuando se registra un service worker, el navegador detecta si el service worker es nuevo (ya sea porque es diferente del anterior o porque no hay un service worker registrado para este sitio). Si el service worker es nuevo (como ocurre en este caso), el navegador lo instalará.

El service worker emite un evento activate cuando toma el control de la página. El código anterior registra un mensaje aquí, pero este evento se usa a menudo para actualizar las cachés.

Solo un service worker puede estar activo a la vez para un alcance determinado (consulta el alcance del service worker), por lo que un service worker recién instalado no se activará hasta que el service worker existente deje de estar en uso. Por eso, todas las páginas controladas por un service worker deben cerrarse antes de que un nuevo service worker pueda apropiarse. Dado que anulamos el registro del service worker existente, el nuevo service worker se activó de inmediato.

Nota: Actualizar la página no es suficiente para transferir el control a un nuevo service worker, porque la página nueva se solicitará antes de que se descargue la actual y no habrá un momento en que el service worker anterior no esté en uso.

Nota: También puedes activar manualmente un nuevo service worker con algunos navegadores y una herramienta para desarrolladores de manera programática con skipWaiting(), que analizaremos en la sección 3.4.

Actualiza el service worker

Agrega el siguiente comentario en cualquier parte de service-worker.js:

// I'm a new service worker

Guarda el archivo y actualiza la página. Observa los registros de la consola. Observa que el nuevo service worker se instala, pero no se activa. En Chrome, puede ver el service worker en espera en la pestaña Application de DevTools.

Cierra todas las páginas asociadas con el service worker. Luego, vuelve a abrir el localhost:8081/. El registro de la consola debería indicar que se activó el nuevo service worker.

Nota: Si obtienes resultados inesperados, asegúrate de que la caché HTTP esté inhabilitada en las herramientas para desarrolladores.

Explicación

El navegador detecta una diferencia de bytes entre el archivo del service worker nuevo y el existente (debido al comentario agregado), por lo que se instala el service worker nuevo. Debido a que solo un service worker puede estar activo por vez (para un alcance determinado), a pesar de que el service worker nuevo esté instalado, no se activará hasta que el service worker existente deje de estar en uso. Al cerrar todas las páginas bajo el control del service worker anterior, podemos activar el nuevo service worker.

Cómo omitir la fase de espera

Es posible que un nuevo service worker se active inmediatamente, incluso si hay uno existente, omitiendo la fase de espera.

En service-worker.js, agrega una llamada a skipWaiting en el objeto de escucha de eventos install:

self.skipWaiting();

Guarda el archivo y actualiza la página. Ten en cuenta que el nuevo service worker se instala y se activa de inmediato, incluso si un service worker anterior tenía el control.

Explicación

El método skipWaiting() permite que un service worker se active apenas finalice la instalación. El objeto de escucha de eventos de instalación es un lugar común para colocar la llamada skipWaiting(), pero se puede llamar en cualquier lugar durante la fase de espera o antes. Consulta esta documentación para obtener más información sobre cuándo y cómo usar skipWaiting(). Durante el resto del lab, podemos probar un nuevo código de service worker sin cancelar el registro del service worker de forma manual.

Más información

Los service workers pueden actuar como proxy entre tu app web y la red.

Agreguemos un objeto de escucha de recuperación para interceptar las solicitudes de nuestro dominio.

Agrega el siguiente código a service-worker.js:

self.addEventListener('fetch', event => {
  console.log('Fetching:', event.request.url);
});

Guarda la secuencia de comandos y actualiza la página para instalar y activar el service worker actualizado.

Revise la consola y observe que no se hayan registrado eventos de recuperación. Actualiza la página y vuelve a verificar la consola. Deberías ver los eventos de recuperación esta vez para la página y sus elementos (como CSS).

Haga clic en los vínculos a Otra página, Otra página y Atrás.

En la consola, verás eventos de recuperación para cada una de las páginas y sus elementos. ¿Tiene sentido todos los registros?

Nota: Si visitas una página y no tienes inhabilitada la caché HTTP, es posible que los elementos de CSS y JavaScript se almacenen en caché de forma local. Si esto ocurre, no verás eventos de recuperación de estos recursos.

Explicación

El service worker recibe un evento de recuperación por cada solicitud HTTP que realiza el navegador que está dentro de su alcance. El objeto fetch event contiene la solicitud. Detectar eventos de recuperación en el service worker es similar a detectar eventos de clic en el DOM, En nuestro código, cuando se produce un evento de recuperación, registramos la URL solicitada en la consola (en la práctica, también podemos crear y mostrar nuestra propia respuesta personalizada con recursos arbitrarios).

¿Por qué no hubo ningún registro de eventos de recuperación en la primera actualización? De forma predeterminada, los eventos de recuperación de una página no pasan por un service worker, a menos que la solicitud de la página en sí lo haga. Esto garantiza la coherencia en tu sitio. Si una página se carga sin el service worker, también lo hacen sus subrecursos.

Más información

Código de solución

Para obtener una copia del código de trabajo, navega a la carpeta 04-intercepting-network-requests/.

Los service workers tienen alcance. El alcance del service worker determina desde qué rutas de acceso intercepta las solicitudes.

Cómo buscar el permiso

Actualiza el código de registro en index.html con lo siguiente:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('SW registered with scope:', registration.scope);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

Actualice el navegador. Observa que la consola muestra el alcance del service worker (en este caso, es http://localhost:8081/).

Explicación

La promesa que muestra register() se resuelve en el objeto de registro, que contiene el alcance del service worker.

El alcance predeterminado es la ruta de acceso al archivo del service worker y se extiende a todos los directorios inferiores. Entonces, un service worker en el directorio raíz de una app controla las solicitudes de todos los archivos de la app.

Cómo mover el service worker

Mueve service-worker.js al directorio below/ y actualiza la URL del service worker en el código de registro en index.html.

Cancela el registro del service worker actual en el navegador y actualiza la página.

La consola muestra que el alcance del service worker ahora es http://localhost:8081/below/. En Chrome, también puedes ver el alcance del service worker en la pestaña de la aplicación de las Herramientas para desarrolladores:

En la página principal, haz clic en Otra página, en Otra página y en Atrás. ¿Qué solicitudes de recuperación se registran? ¿Cuáles no?

Explicación

El alcance predeterminado del service worker es la ruta de acceso al archivo del service worker. Dado que el archivo del service worker ahora está en below/, ese es su alcance. Ahora, la consola solo registra eventos de recuperación para another.html, another.css y another.js, ya que estos son los únicos recursos dentro del alcance del service worker.

Configura un alcance arbitrario

Mueve el service worker hacia el directorio raíz del proyecto (app/) y actualiza la URL del service worker en el código de registro de index.html.

Usa la referencia en MDN para establecer el alcance del service worker en el directorio below/ mediante el parámetro opcional en register().

Cancela el registro del service worker y actualiza la página. Haz clic en Otra página, Otra página y Atrás.

Una vez más, la consola muestra que el alcance del service worker ahora es http://localhost:8081/below/ y registra los eventos de recuperación solo para another.html, another.css y another.js.

Explicación

Es posible establecer un alcance arbitrario pasando un parámetro adicional cuando se registra, por ejemplo:

navigator.serviceWorker.register('/service-worker.js', {
  scope: '/kitten/'
});

En el ejemplo anterior, el alcance del service worker se establece en /kitten/. El service worker intercepta las solicitudes de las páginas de /kitten/ y /kitten/lower/, pero no de páginas como /kitten o /.

Nota: No puedes establecer un alcance arbitrario que esté por encima de la ubicación real del service worker. Sin embargo, si el service worker está activo en un cliente que recibe el encabezado Service-Worker-Allowed, puedes especificar un alcance máximo para ese service worker por encima de la ubicación del service worker.

Más información

Código de solución

Para obtener una copia del código de trabajo, navega a la carpeta solution/.

Ahora tienes un service worker simple en funcionamiento, y comprendes el ciclo de vida del service worker.

Más información

Ciclo de vida del service worker

Para ver todos los codelabs del curso de capacitación sobre AWP, consulta el codelab de bienvenida del curso.