Cree una PWA con Webpack y Workbox
Publicado: 2022-03-10Una aplicación web progresiva (PWA) es un sitio que utiliza tecnología moderna para ofrecer experiencias similares a las de una aplicación en la web. Es un término general para las nuevas tecnologías, como el "manifiesto de la aplicación web", el "trabajador de servicios" y más. Cuando se unen, estas tecnologías le permiten ofrecer experiencias de usuario rápidas y atractivas con su sitio web.
Este artículo es un tutorial paso a paso para agregar un trabajador de servicio a un sitio web existente de una página. El trabajador del servicio le permitirá hacer que su sitio web funcione sin conexión y también notificará a sus usuarios sobre las actualizaciones de su sitio. Tenga en cuenta que esto se basa en un proyecto pequeño que se incluye con Webpack, por lo que usaremos el complemento Workbox Webpack (Workbox v4).
Usar una herramienta para generar su trabajador de servicio es el enfoque recomendado, ya que le permite administrar su caché de manera eficiente. Usaremos Workbox, un conjunto de bibliotecas que facilitan la generación de su código de trabajador de servicio, para generar nuestro trabajador de servicio en este tutorial.
Dependiendo de su proyecto, puede usar Workbox de tres maneras diferentes:
- Hay disponible una interfaz de línea de comandos que le permite integrar Workbox en cualquier aplicación que tenga;
- Hay disponible un módulo Node.js que le permite integrar Workbox en cualquier herramienta de compilación de Node, como gulp o grunt;
- Hay disponible un complemento de paquete web que le permite integrarse fácilmente con un proyecto creado con Webpack.
Webpack es un paquete de módulos. Para simplificar, puede considerarlo como una herramienta que administra sus dependencias de JavaScript. Le permite importar código JavaScript de bibliotecas y agrupa su JavaScript en uno o varios archivos.
Para comenzar, clone el siguiente repositorio en su computadora:
git clone [email protected]:jadjoubran/workbox-tutorial-v4.git cd workbox-tutorial-v4 npm install npm run dev
A continuación, navegue hasta https://localhost:8080/
. Debería poder ver la aplicación de moneda que usaremos a lo largo de este tutorial:
Comience con un shell de aplicación
El shell de la aplicación (o 'shell de la aplicación') es un patrón inspirado en las aplicaciones nativas. Ayudará a darle a su aplicación un aspecto más nativo. Simplemente proporciona a la aplicación un diseño y una estructura sin datos: una pantalla de transición que tiene como objetivo mejorar la experiencia de carga de su aplicación web.
Estos son algunos ejemplos de shells de aplicaciones de aplicaciones nativas:
Y aquí hay ejemplos de shells de aplicaciones de PWA:
A los usuarios les gusta la experiencia de carga de los shells de aplicaciones porque desprecian las pantallas en blanco. Una pantalla en blanco hace que el usuario sienta que el sitio web no se está cargando. Les hace sentir como si el sitio web estuviera atascado.
Los shells de la aplicación intentan pintar la estructura de navegación de la aplicación lo antes posible, como la barra de navegación, la barra de pestañas y un cargador que indica que el contenido que ha solicitado se está cargando.
Entonces, ¿cómo se construye una aplicación Shell?
El patrón de shell de la aplicación prioriza la carga de HTML, CSS y JavaScript que se procesarán primero. Esto significa que debemos dar prioridad total a esos recursos, por lo tanto, debe alinear esos activos. Entonces, para crear un shell de aplicación, simplemente tiene que alinear el HTML, CSS y JavaScript que son responsables del shell de la aplicación. Por supuesto, no debe alinear todo, sino mantenerse dentro de un límite de alrededor de 30 a 40 KB en total.
Puede ver el shell de la aplicación integrado en index.html . Puede inspeccionar el código fuente consultando el archivo index.html y puede obtener una vista previa en el navegador eliminando el elemento <main>
en las herramientas de desarrollo:
¿Funciona sin conexión?
¡Simulemos desconectarnos! Abra DevTools, vaya a la pestaña de red y marque la casilla de verificación 'Sin conexión'. Cuando vuelva a cargar la página, verá que obtendremos la página sin conexión del navegador.
Esto se debe a que la solicitud inicial a /
(que cargará el archivo index.html ) fallará porque Internet está desconectado. La única forma en que podemos recuperarnos de esa falla de solicitud es tener un trabajador de servicio.
Visualicemos la solicitud sin un service worker:
Un trabajador de servicio es un proxy de red programable, lo que significa que se encuentra entre su página web e Internet. Esto le permite controlar las solicitudes de red entrantes y salientes.
Esto es beneficioso porque ahora podemos redirigir esta solicitud fallida al caché (suponiendo que tenemos el contenido en el caché).
Un trabajador de servicio también es un tipo de trabajador web, lo que significa que se ejecuta por separado de su página principal y no tiene acceso ni a la window
ni al objeto de document
.
Precachear el shell de la aplicación
Para que nuestra aplicación funcione sin conexión, vamos a comenzar por almacenar en caché el shell de la aplicación.
Entonces, comencemos instalando el complemento Webpack Workbox:
npm install --save-dev workbox-webpack-plugin
Luego vamos a abrir nuestro archivo index.js y registrar el trabajador del servicio:
if ("serviceWorker" in navigator){ window.addEventListener("load", () => { navigator.serviceWorker.register("/sw.js"); }) }
A continuación, abra el archivo webpack.config.js y configuremos el complemento del paquete web de Workbox:
//add at the top const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); //add inside the plugins array: plugins: [ … , new WorkboxWebpackPlugin.InjectManifest({ swSrc: "./src/src-sw.js", swDest: "sw.js" }) ]
Esto le indicará a Workbox que use nuestro archivo ./src/src-sw.js como base. El archivo generado se llamará sw.js y estará en la carpeta dist
.
Luego cree un archivo ./src/src-sw.js en el nivel raíz y escriba lo siguiente dentro de él:
workbox.precaching.precacheAndRoute(self.__precacheManifest);
Nota : la variable self.__precacheManifest
se importará desde un archivo que Workbox generará dinámicamente.
Ahora está listo para compilar su código con npm run build
y Workbox generará dos archivos dentro de la carpeta dist
:
- precache-manifest.66cf63077c7e4a70ba741ee9e6a8da29.js
- sw.js
El sw.js importa la caja de trabajo de CDN, así como el precache-manifest.[chunkhash].js .
//precache-manifest.[chunkhash].js file self.__precacheManifest = (self.__precacheManifest || []).concat([ "revision": "ba8f7488757693a5a5b1e712ac29cc28", "url": "index.html" }, "url": "main.49467c51ac5e0cb2b58e.js" ]);
El manifiesto de precaché enumera los nombres de los archivos que fueron procesados por webpack y que terminan en su carpeta dist
. Usaremos estos archivos para precachéarlos en el navegador. Esto significa que cuando su sitio web se carga por primera vez y registra al trabajador del servicio, almacenará en caché estos activos para que puedan usarse la próxima vez.
También puede notar que algunas entradas tienen una 'revisión' mientras que otras no. Esto se debe a que, a veces, la revisión se puede inferir del chunkhash del nombre del archivo. Por ejemplo, echemos un vistazo más de cerca al nombre de archivo main.49467c51ac5e0cb2b58e.js . Tiene una revisión en el nombre del archivo, que es chunkhash 49467c51ac5e0cb2b58e .
Esto le permite a Workbox comprender cuándo cambian sus archivos para que solo limpie o actualice los archivos que se cambiaron, en lugar de volcar todo el caché cada vez que publica una nueva versión de su trabajador de servicio.
La primera vez que cargue la página, se instalará el trabajador de servicio. Puedes verlo en DevTools. Primero, se solicita el archivo sw.js , que luego solicita todos los demás archivos. Están claramente marcados con el icono de engranaje.
Por lo tanto, Workbox se inicializará y almacenará en caché todos los archivos que se encuentran en el manifiesto de caché previa. Es importante verificar que no tenga archivos innecesarios en el archivo de manifiesto de caché previa , como archivos .map o archivos que no formen parte del shell de la aplicación.
En la pestaña de red, podemos ver las solicitudes provenientes del trabajador del servicio. Y ahora, si intenta desconectarse, el shell de la aplicación ya está precaché, ¡así que funciona incluso si estamos desconectados!
Caché de rutas dinámicas
¿Notó que cuando nos desconectamos, el shell de la aplicación funciona pero no nuestros datos? Esto se debe a que estas llamadas a la API no forman parte del shell de la aplicación precaché . Cuando no hay conexión a Internet, estas solicitudes fallarán y el usuario no podrá ver la información de la moneda.
Sin embargo, estas solicitudes no se pueden almacenar en caché porque su valor proviene de una API. Además, cuando comienza a tener varias páginas, no desea almacenar en caché todas las solicitudes de API de una sola vez. En su lugar, desea almacenarlos en caché cuando el usuario visita esa página.
Los llamamos "datos dinámicos". A menudo incluyen llamadas a la API, así como imágenes y otros activos que se solicitan cuando un usuario realiza una determinada acción en su sitio web (por ejemplo, cuando navega a una página nueva).
Puede almacenarlos en caché utilizando el módulo de enrutamiento de Workbox. Así es cómo:
//add in src/src-sw.js workbox.routing.registerRoute( /https:\/\/api\.exchangeratesapi\.io\/latest/, new workbox.strategies.NetworkFirst({ cacheName: "currencies", plugins: [ new workbox.expiration.Plugin({ maxAgeSeconds: 10 * 60 // 10 minutes }) ] }) );
Esto configurará el almacenamiento en caché dinámico para cualquier URL de solicitud que coincida con la URL https://api.exchangeratesapi.io/latest
.
La estrategia de almacenamiento en caché que usamos aquí se llama NetworkFirst
; hay otros dos que se usan a menudo:
-
CacheFirst
-
StaleWhileRevalidate
CacheFirst
lo buscará primero en el caché. Si no se encuentra, lo obtendrá de la red. StaleWhileRevalidate
irá a la red y al caché al mismo tiempo. Devuelva la respuesta del caché a la página (mientras está en segundo plano) usará la nueva respuesta de red para actualizar el caché para la próxima vez que se use.
Para nuestro caso de uso, tuvimos que optar por NetworkFirst
porque estamos tratando con tipos de cambio que cambian muy a menudo. Sin embargo, cuando el usuario se desconecta, al menos podemos mostrarle las tarifas tal como estaban hace 10 minutos; es por eso que usamos el complemento de caducidad con maxAgeSeconds
establecido en 10 * 60
segundos.
Administrar actualizaciones de aplicaciones
Cada vez que un usuario cargue su página, el navegador ejecutará el código navigator.serviceWorker.register
aunque el trabajador de servicio ya esté instalado y ejecutándose. Esto permite que el navegador detecte si hay una nueva versión del trabajador del servicio. Cuando el navegador nota que el archivo no ha cambiado, simplemente omite la llamada de registro. Una vez que ese archivo cambia, el navegador entiende que hay una nueva versión del trabajador de servicio, por lo que instala el nuevo trabajador de servicio en paralelo al trabajador de servicio que se está ejecutando actualmente .
Sin embargo, se detiene en la fase de installed/waiting
porque solo se puede activar un trabajador de servicio al mismo tiempo.
Solo cuando se cierran todas las ventanas del navegador controladas por el trabajador de servicio anterior, entonces es seguro que se active el nuevo trabajador de servicio.
También puede controlarlo manualmente llamando a skipWaiting()
(o self.skipWaiting()
ya que self
es el contexto de ejecución global en el trabajador del servicio). Sin embargo, la mayoría de las veces solo debe hacerlo después de preguntarle al usuario si desea obtener la última actualización.
Afortunadamente, workbox-window
nos ayuda a lograr esto. Es una nueva biblioteca de ventanas introducida en Workbox v4 que tiene como objetivo simplificar las tareas comunes en el lado de la ventana.
Comencemos por instalarlo con lo siguiente:
npm install workbox-window
A continuación, importe Workbox
en la parte superior del archivo index.js :
import { Workbox } from "workbox-window";
Luego reemplazaremos nuestro código de registro con el siguiente:
if ("serviceWorker" in navigator) { window.addEventListener("load", () => { const wb = new Workbox("/sw.js"); wb.register(); }); }
Luego encontraremos el botón de actualización que tiene la IDworkbox-waiting
de la caja de trabajo:
//add before the wb.register() const updateButton = document.querySelector("#app-update"); // Fires when the registered service worker has installed but is waiting to activate. wb.addEventListener("waiting", event => { updateButton.classList.add("show"); updateButton.addEventListener("click", () => { // Set up a listener that will reload the page as soon as the previously waiting service worker has taken control. wb.addEventListener("controlling", event => { window.location.reload(); }); // Send a message telling the service worker to skip waiting. // This will trigger the `controlling` event handler above. wb.messageSW({ type: "SKIP_WAITING" }); }); });
Este código mostrará el botón de actualización cuando haya una nueva actualización (es decir, cuando el trabajador del servicio esté en estado de espera) y enviará un mensaje SKIP_WAITING
al trabajador del servicio.
Necesitaremos actualizar el archivo del trabajador del servicio y manejar el evento SKIP_WAITING
de modo que llame a skipWaiting
:
//add in src-sw.js addEventListener("message", event => { if (event.data && event.data.type === "SKIP_WAITING") { skipWaiting(); });
Ahora ejecute npm run dev
y luego vuelva a cargar la página. Ingrese a su código y actualice el título de la barra de navegación a "Navbar v2". Vuelva a cargar la página y debería poder ver el icono de actualización.
Terminando
Nuestro sitio web ahora funciona sin conexión y puede informar al usuario sobre nuevas actualizaciones. Sin embargo, tenga en cuenta que el factor más importante al crear una PWA es la experiencia del usuario. Concéntrese siempre en crear experiencias que sean fáciles de usar para sus usuarios. Nosotros, como desarrolladores, tendemos a entusiasmarnos demasiado con la tecnología y, a menudo, terminamos olvidándonos de nuestros usuarios.
Si desea ir un paso más allá, puede agregar un manifiesto de aplicación web que permitirá a sus usuarios agregar el sitio a su pantalla de inicio. Y si desea obtener más información sobre Workbox, puede encontrar la documentación oficial en el sitio web de Workbox.
Lectura adicional en SmashingMag:
- ¿Se puede ganar más dinero con una aplicación móvil o una PWA?
- Una guía extensa para aplicaciones web progresivas
- Native y PWA: ¡Opciones, no retadores!
- Construyendo una PWA usando Angular 6