Cómo optimizar aplicaciones web progresivas: ir más allá de lo básico
Publicado: 2022-03-10Las aplicaciones web progresivas (PWA) siguen ganando popularidad en 2020. No es una sorpresa, considerando los beneficios de tasas de conversión más altas, participación del cliente, velocidad de carga de página reducida y costos de desarrollo y gastos generales más bajos.
Podemos ver empresas respetadas que también disfrutan del éxito con sus PWA, como Twitter, Uber, Tinder, Pinterest y Forbes. Y todos se jactan de los enormes beneficios de implementar las aplicaciones progresivas.
La buena noticia es que desarrollar una PWA no es algo que solo puedan permitirse las empresas de gran presupuesto. Estas aplicaciones sirven a las empresas medianas y pequeñas por igual y no son tan complicadas de crear.
Puede encontrar una guía completa para principiantes sobre aplicaciones web progresivas en Smashing Magazine que se enfoca en construir el núcleo de las PWA.
Sin embargo, demos un paso más y aprendamos a implementar cualidades modernas en las PWA, como la funcionalidad fuera de línea, la optimización basada en la red, la experiencia del usuario entre dispositivos, las capacidades de SEO y las notificaciones y solicitudes no intrusivas. También encontrará código de ejemplo o referencias a guías más específicas para que pueda implementar estos consejos en su PWA.
Una descripción general rápida de las aplicaciones web progresivas (PWA)
No nos saltemos los conceptos básicos y repasemos rápidamente el corazón de las PWA.
¿Qué es una PWA?
“Las aplicaciones web progresivas (PWA) se construyen y mejoran con API modernas para brindar capacidades, confiabilidad e instalación mejoradas mientras llegan a cualquier persona, en cualquier lugar, en cualquier dispositivo con una base de código única”.
— Los desarrolladores de Google
En otras palabras, las PWA son sitios web que los usuarios pueden usar como aplicaciones independientes. Son diferentes de las aplicaciones nativas principalmente porque las PWA no requieren instalación y se pueden usar con varios dispositivos; las aplicaciones nativas están diseñadas principalmente para dispositivos móviles.
¿Cómo funcionan las PWA?
El núcleo de una PWA consta de tres componentes: un manifiesto de aplicación web, trabajadores de servicio y un shell de aplicación. Puede encontrar instrucciones detalladas para construirlos en la guía para principiantes mencionada anteriormente.
Esto es lo que hacen estos componentes.
Manifiesto de la aplicación web
El manifiesto de la aplicación web es el núcleo para hacer que un sitio web se ejecute como una aplicación independiente en modo de pantalla completa. Puede definir cómo se ve el PWA, optimizarlo para diferentes dispositivos y asignar un ícono que se muestra después de la instalación de la aplicación.
Trabajadores de servicios
Los trabajadores del servicio permiten el uso sin conexión de la PWA al obtener datos almacenados en caché o informar al usuario sobre la ausencia de una conexión a Internet. Los trabajadores del servicio también recuperan los datos más recientes una vez que se restablece la conexión del servidor.
Arquitectura de shell de aplicación
El shell de la aplicación es lo que ven los usuarios cuando acceden a una PWA. Es el mínimo de HTML, CSS y JavaScript que se requiere para potenciar la interfaz de usuario. Al desarrollar una PWA, puede almacenar en caché los recursos y activos del shell de la aplicación en el navegador.
Implementación de características modernas en su PWA
Además de las características principales, las PWA modernas adoptan características adicionales que impulsan aún más a sus usuarios hacia una experiencia de usuario más extraordinaria.
Veamos algunas características modernas específicas de una PWA y aprendamos a agregarlas a su PWA. Los desarrolladores de Google consideran que las siguientes cualidades son excelentes adiciones a la PWA básica.
La aplicación funciona sin conexión como lo hace en línea
Al crear su PWA, también puede desarrollar una página sin conexión personalizada como parte del núcleo. Sin embargo, es mucho más fácil de usar si su PWA continúa funcionando incluso sin una conexión a Internet, hasta cierto punto en el que la conexión se vuelve necesaria. De lo contrario, la experiencia del usuario podría ser tan frustrante como la terrible experiencia de Ankita Masand al pedir un pastel, como describe en su artículo sobre los puntos débiles de las PWA.
Puede lograr una experiencia de usuario más significativa mediante el uso de contenido en caché, sincronización en segundo plano y pantallas esqueléticas. Veamos cada uno.
Contenido en caché con IndexedDB
IndexedDB
es un sistema de almacenamiento NoSQL en el navegador que puede usar para almacenar en caché y recuperar los datos necesarios para que su PWA funcione sin conexión.
Sin embargo, no todos los navegadores admiten IndexedDB
, por lo que lo primero que debe hacer es verificar si el navegador del usuario lo admite.
if (!('indexedDB' in window)) { console.log('This browser doesn\'t support IndexedDB'); return; }
Después de esto, puede crear contenido en caché con la API de IndexedDB. Este es un ejemplo de los desarrolladores de Google de cómo abrir una base de datos, agregar una tienda de objetos y agregar un elemento a esta tienda.
var db; var openRequest = indexedDB.open('test_db', 1); openRequest.onupgradeneeded = function(e) { var db = e.target.result; console.log('running onupgradeneeded'); if (!db.objectStoreNames.contains('store')) { var storeOS = db.createObjectStore('store', {keyPath: 'name'}); } }; openRequest.onsuccess = function(e) { console.log('running onsuccess'); db = e.target.result; addItem(); }; openRequest.onerror = function(e) { console.log('onerror!'); console.dir(e); }; function addItem() { var transaction = db.transaction(['store'], 'readwrite'); var store = transaction.objectStore('store'); var item = { name: 'banana', price: '$2.99', description: 'It is a purple banana!', created: new Date().getTime() }; var request = store.add(item); request.onerror = function(e) { console.log('Error', e.target.error.name); }; request.onsuccess = function(e) { console.log('Woot! Did it'); }; }
sincronización de fondo
Si su PWA sincroniza datos en segundo plano, el usuario puede realizar acciones mientras está desconectado, que luego se ejecutan cuando se restaura la conexión a Internet. Un ejemplo simple es una aplicación de mensajería. Un usuario puede enviar un mensaje cuando está desconectado sin necesidad de esperar hasta que se envíe; la sincronización en segundo plano envía automáticamente el mensaje cuando se restablece la conexión.
Este es un ejemplo de cómo desarrollar una función de sincronización en segundo plano de Jake Archibald.
// Register your service worker: navigator.serviceWorker.register('/sw.js'); // Then later, request a one-off sync: navigator.serviceWorker.ready.then(function(swRegistration) { return swRegistration.sync.register('myFirstSync'); });
Luego escuche el evento en /sw.js
:
self.addEventListener('sync', function(event) { if (event.tag == 'myFirstSync') { event.waitUntil(doSomeStuff()); } });
Pantallas de esqueleto
Una de las principales ventajas de usar pantallas esqueléticas es que los usuarios perciben que la aplicación funciona en lugar de permanecer inactiva. Si bien el usuario no tiene una conexión, la pantalla esquelética dibuja la interfaz sin contenido, que luego se llena una vez que se restablece la conexión.
Code My UI tiene algunos fragmentos de código excelentes disponibles que puede usar para crear una pantalla de esqueleto para su PWA.
Optimización basada en el uso de la red
Un beneficio central de un PWA es que proporciona una experiencia más rápida para los usuarios. Puede optimizar aún más la velocidad de carga haciendo que la PWA use la red de caché primero, priorizando los recursos y use la carga adaptativa según la calidad de la red.
Veamos cómo puede desarrollarlos para su PWA.
Caché primero, luego red
El uso del contenido en caché primero permite que su PWA funcione sin conexión y allana el camino para que los usuarios accedan al contenido incluso en áreas de baja cobertura de red. Puede hacerlo creando un trabajador de servicio para almacenar en caché el contenido y luego recuperarlo.
Aquí hay un ejemplo de Jeff Posnick sobre el almacenamiento en caché de HTML estático usando trabajadores de servicio.
self.addEventListener('fetch', event => { if (event.request.mode === 'navigate') { // See /web/fundamentals/getting-started/primers/async-functions // for an async/await primer. event.respondWith(async function() { // Optional: Normalize the incoming URL by removing query parameters. // Instead of https://example.com/page?key=value, // use https://example.com/page when reading and writing to the cache. // For static HTML documents, it's unlikely your query parameters will // affect the HTML returned. But if you do use query parameters that // uniquely determine your HTML, modify this code to retain them. const normalizedUrl = new URL(event.request.url); normalizedUrl.search = ''; // Create promises for both the network response, // and a copy of the response that can be used in the cache. const fetchResponseP = fetch(normalizedUrl); const fetchResponseCloneP = fetchResponseP.then(r => r.clone()); // event.waitUntil() ensures that the service worker is kept alive // long enough to complete the cache update. event.waitUntil(async function() { const cache = await caches.open('my-cache-name'); await cache.put(normalizedUrl, await fetchResponseCloneP); }()); // Prefer the cached response, falling back to the fetch response. return (await caches.match(normalizedUrl)) || fetchResponseP; }()); } });
Priorización de recursos
De forma predeterminada, las PWA tienen un mayor rendimiento que las aplicaciones nativas similares debido a su naturaleza liviana. Además, dado que las PWA usan la memoria caché del navegador, es posible indicar qué recursos tienen prioridad y deben procesarse incluso antes de que se usen. Esto funciona principalmente con elementos estáticos, ya que el contenido dinámico debe actualizarse antes de que se obtenga.
Puede especificar la prioridad de los elementos utilizando la cadena <link>
en el HTML. También puede especificar archivos de servidores de terceros utilizando rel=”preconnect”
y rel=”dns-prefetch.”
Maximiliano Firtman da un ejemplo simple de esto al priorizar las fuentes web dentro del motor del navegador:
<link rel=”preload” as=”font” href=”font.woff” crossorigin>
Implementación de carga adaptativa
Las velocidades de Internet de Wi-Fi y 4G no están disponibles en todas partes, y los usuarios siguen accediendo a Internet con conexiones 2G y 3G. Dado que desea que su PWA sea accesible para la mayor cantidad de personas posible, es posible que también desee optimizarlo para que funcione a velocidades de Internet más bajas.
Puede lograr esto implementando la carga adaptativa, que carga los elementos de las PWA en función del tipo de conexión que tenga el usuario.
La forma más sencilla es usar la herramienta Workbox de Google, que incluye numerosos complementos listos para usar para estrategias de almacenamiento en caché.
Suponga que desea definir una estrategia de almacenamiento en caché personalizada. Así es como puede hacerlo como un ejemplo de Demian Renzulli y Jeff Posnick:
const adaptiveLoadingPlugin = { requestWillFetch: async ({request}) => { const urlParts = request.url.split('/'); let imageQuality; switch ( navigator && navigator.connection ? navigator.connection.effectiveType : '' ) { //... case '3g': imageQuality = 'q_30'; break; //... } const newUrl = urlParts .splice(urlParts.length - 1, 0, imageQuality) .join('/') .replace('.jpg', '.png'); const newRequest = new Request(newUrl.href, {headers: request.headers}); return newRequest; }, };
A continuación, pase el complemento a una estrategia de cacheFirst
que contenga una expresión regular para hacer coincidir las URL de las imágenes (por ejemplo /img/
):
workbox.routing.registerRoute( new RegExp('/img/'), workbox.strategies.cacheFirst({ cacheName: 'images', plugins: [ adaptiveLoadingPlugin, workbox.expiration.Plugin({ maxEntries: 50, purgeOnQuotaError: true, }), ], }), );
Excelente experiencia de usuario en todas las plataformas
Un excelente PWA funciona a la perfección en navegadores, dispositivos móviles y tabletas. Si bien el uso de un dispositivo Android es la forma más popular (con una participación de mercado del 38,9 %) de acceder a Internet, la optimización de su aplicación para todas las plataformas es parte del desarrollo de las funciones principales de las PWA.
Puede tomar medidas adicionales para aumentar la facilidad de uso y ofrecer una excelente experiencia de usuario, como reducir los saltos cuando se carga su PWA y asegurarse de que su PWA funcione con cualquier método de entrada.
Así es como puede abordar cada uno de esos aspectos.
Reducción de la carga de contenido "con saltos"
Incluso con Internet de alta velocidad, el contenido del sitio puede cambiar mientras se carga porque los elementos del sitio se cargan en orden. Este efecto es aún peor con velocidades de conexión más lentas y perjudica gravemente la experiencia del usuario.
Los elementos más comunes que hacen que el contenido se desplace durante la carga son las imágenes, ya que generalmente son más grandes y no son una prioridad al cargar contenido. Puede abordar este problema con la "carga diferida" mediante el uso de imágenes de marcador de posición más pequeñas que puede priorizar después de que se represente la estructura HTML.
Aquí hay un ejemplo de los desarrolladores de Mozilla sobre cómo puede agregar una imagen liviana que se cargue primero antes de la imagen real en JavaScript:
<img src='data/img/placeholder.png' data-src='data/img/SLUG.jpg' alt='NAME'>
El archivo app.js
procesa los atributos data-src de la siguiente manera:
let imagesToLoad = document.querySelectorAll('img[data-src]'); const loadImages = (image) => { image.setAttribute('src', image.getAttribute('data-src')); image.onload = () => { image.removeAttribute('data-src'); }; };
Y luego crea un bucle:
imagesToLoad.forEach((img) => { loadImages(img); });
También puede consultar una guía completa en Smashing Magazine sobre cómo reducir el salto de contenido con otros elementos.
PWA funciona con cualquier método de entrada
Hemos cubierto cómo deberían funcionar las PWA con una variedad de dispositivos diferentes. Para dar un paso más, también debe tener en cuenta otros métodos de entrada que los usuarios pueden usar en estos dispositivos, como el tacto, el mouse y el lápiz óptico.
Agregar la API de Pointer Events a su PWA resuelve principalmente esta pregunta. Así es como puede abordarlo según los desarrolladores de Google.
Primero, verifique si el navegador admite los eventos de puntero:
if (window.PointerEvent) { // Yay, we can use pointer events! } else { // Back to mouse and touch events, I guess. }
A continuación, puede definir las acciones que pueden realizar varios métodos de entrada:
switch(ev.pointerType) { case 'mouse': // Do nothing. break; case 'touch': // Allow drag gesture. break; case 'pen': // Also allow drag gesture. break; default: // Getting an empty string means the browser doesn't know // what device type it is. Let's assume mouse and do nothing. break; }
Dado que la mayoría de los navegadores ya tienen funciones táctiles, no necesitará agregar nada más.
Visible a través de la búsqueda
Uno de los beneficios clave de las PWA sobre una aplicación nativa es que la PWA es un sitio web por naturaleza y los motores de búsqueda pueden indexarlos. Esto le permite implementar estrategias de SEO para hacer que su contenido de PWA sea más reconocible.
Puede comenzar asegurándose de que cada URL en su PWA tenga un título descriptivo único y una meta descripción, que es la línea de base de cualquier actividad de optimización SEO.
Veamos algunos otros pasos que puede seguir para que su PWA pueda buscarse.
Analiza la encontrabilidad de tu PWA
Google tiene una excelente herramienta en su Consola de Búsqueda que analiza tu sitio (PWA) y reporta los resultados. Puede usarlo para ejecutar un escaneo básico de su sitio y descubrir cualquier punto débil que luego puede comenzar a reparar.
Alternativamente, puede usar Lighthouse en el navegador Chrome para ejecutar una auditoría de SEO.
Primero, navegue a la URL de destino. Luego presione Control+Shift+J
(o Command+Option+J
en Mac) que abre el menú de herramientas del desarrollador. Elija la pestaña Lighthouse, marque la casilla de categoría SEO y genere el informe.
Usar datos estructurados
El motor de búsqueda de Google utiliza datos estructurados para comprender el propósito del contenido de su página web.
Los datos estructurados son un formato estandarizado para proporcionar información sobre una página y clasificar el contenido de la página; por ejemplo, en una página de recetas, cuáles son los ingredientes, el tiempo y la temperatura de cocción, las calorías, etc.
Antes de comenzar a codificar, Google también ha elaborado una lista útil de errores comunes de datos estructurados y pautas relevantes para corregirlos. Estudiar este material debería darle una buena base de referencia de lo que debe evitar.
Frederick O'Brien ha escrito una excelente guía en Smashing Magazine, Baking Structured Data Into The Design Process , que describe cómo crear datos estructurados desde el principio.
Notificaciones fáciles de usar y solicitudes de permisos
Por último, pero no menos importante, puede aumentar la experiencia del usuario optimizando las notificaciones y las solicitudes de permiso, para que sirvan a sus usuarios, en lugar de ser simplemente confusos y molestos.
Si bien generalmente puede confiar en el sentido común, existen consejos prácticos que también puede implementar, como crear notificaciones automáticas no intrusivas y brindarle al usuario la opción de darse de baja de los mensajes.
Sutiles solicitudes de permiso para la comunicación.
Hay dos formas modernas de automatizar la comunicación entre un sitio web y un usuario: chatbots y notificaciones.
En un contexto de PWA, la principal ventaja de un chatbot es que no requiere el permiso del usuario para interactuar con el usuario. Sin embargo, según la aplicación de chatbot que utilice, el usuario podría perderse el mensaje sutil. Las notificaciones, por otro lado, requieren el permiso del usuario pero son mucho más visibles.
Dado que puede agregar un chatbot como una aplicación de terceros separada, concentrémonos en crear una notificación automática fácil de usar. En caso de que necesite una guía sobre cómo crear una notificación push en primer lugar, aquí hay una excelente de Indrek Lasn.
La forma más sencilla de crear una solicitud de permiso no intrusiva es mediante una solicitud doble. Esto significa que incluye una interacción personalizada en su sitio además de la predeterminada del sistema operativo del usuario.
Matt Gaunt ofrece ilustraciones perfectas para este efecto con las siguientes imágenes.
Aquí está la solicitud de permiso de notificación predeterminada que no proporciona contexto:
Y aquí está la interacción personalizada agregada antes del permiso de notificación predeterminado descrito anteriormente:
Al agregar una alerta personalizada antes de la predeterminada del sistema operativo, puede describir el propósito de la notificación más claramente al usuario. Esto aumenta la posibilidad de que el usuario opte por las notificaciones de su sitio.
Permitir que el usuario opte por no recibir notificaciones
Para un usuario, deshabilitar las notificaciones automáticas de un sitio es bastante molesto, independientemente del dispositivo que esté usando. Por lo tanto, dar al usuario la opción de optar por no recibir los mensajes contribuye en gran medida a la experiencia del usuario.
Aquí hay un ejemplo de Matt Gaunt sobre cómo implementar una función de cancelación de suscripción en su código y UI:
Primero, defina el detector de clics del pushButton
:
pushButton.addEventListener('click', function() { pushButton.disabled = true; if (isSubscribed) { unsubscribeUser(); } else { subscribeUser(); } });
Luego, agregue una nueva función:
function unsubscribeUser() { swRegistration.pushManager.getSubscription() .then(function(subscription) { if (subscription) { // TODO: Tell application server to delete subscription return subscription.unsubscribe(); } }) .catch(function(error) { console.log('Error unsubscribing', error); }) .then(function() { updateSubscriptionOnServer(null); console.log('User is unsubscribed.'); isSubscribed = false; updateBtn(); }); }
Así es como se ve en la consola después de implementar con éxito el botón de suscripción para deshabilitar las notificaciones si el usuario lo elige.
Conclusión
Los PWA pueden ser muy poderosos para aumentar el tráfico de su sitio y ampliar la experiencia del usuario. Para optimizar aún más su PWA, puede usar estas soluciones modernas que se describen en este artículo para mejorar el rendimiento y la funcionalidad.
Tampoco es necesario implementar todo a la vez. Siéntete libre de elegir las opciones más relevantes, ya que cada una de estas sugerencias te ayuda a mejorar tu PWA.
Más recursos
- Capacitación en aplicaciones web progresivas de Google
- Aplicaciones web progresivas de web.dev
- Aplicaciones web progresivas (PWA) de Mozilla