Web Performance Easy: Google I/O edición 2018

En Google IO 2018, presentamos un resumen de herramientas, bibliotecas y técnicas de optimización que facilitan la mejora del rendimiento de la Web. Aquí los explicamos a través de la app de The Oodles Theatre. También hablamos de nuestros experimentos con carga predictiva y la nueva iniciativa Guess.js.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

Durante el último año, estuvimos bastante ocupados tratando de descubrir cómo hacer que la Web sea más rápida y tenga un mejor rendimiento. Esto nos dio lugar a nuevas herramientas, enfoques y bibliotecas que nos gustaría compartir contigo en este artículo. En la primera parte, te mostraremos algunas técnicas de optimización que usamos en la práctica durante el desarrollo de la app de The Oodles Theatre. En la segunda parte, hablaremos sobre nuestros experimentos con carga predictiva y la nueva iniciativa Guess.js.

Necesidad de rendimiento

Internet se vuelve cada vez más pesada cada año. Si verificamos el estado de la Web, veremos que una página mediana en dispositivos móviles pesa alrededor de 1.5 MB, y que principalmente son imágenes y JavaScript.

El tamaño creciente de los sitios web, junto con otros factores como la latencia de red, las limitaciones de la CPU, los patrones de bloqueo de renderización o el código superfluo de terceros, contribuyen al complicado desafío de rendimiento.

La mayoría de los usuarios califican la velocidad como una posición en la cima de la jerarquía de UX de sus necesidades. Esto no es muy sorprendente, ya que no puedes hacer mucho hasta que una página termine de cargarse. No se puede obtener valor de la página ni admirar su estética.

Pirámide de jerarquía de UX
Figura 1: ¿Qué tan importante es la velocidad para los usuarios? (Speed Matters, Vol. 3)

Sabemos que el rendimiento es importante para los usuarios, pero también puede parecer un secreto para descubrir dónde comenzar la optimización. Afortunadamente, existen herramientas que pueden ayudarte en el camino.

Lighthouse: Una base para el flujo de trabajo de rendimiento

Lighthouse es parte de las Herramientas para desarrolladores de Chrome que te permite realizar una auditoría de tu sitio web y te brinda sugerencias para mejorarlo.

Recientemente, lanzamos muchas auditorías de rendimiento nuevas, que son realmente útiles para el flujo de trabajo de desarrollo diario.

Nuevas auditorías de Lighthouse
Figura 2: Nuevas auditorías de Lighthouse

Veamos un ejemplo práctico para aprovecharlos: la app de Oodles Theatre. Es una pequeña app web de demostración en la que puedes probar algunos de nuestros doodles interactivos de Google y hasta jugar uno o dos juegos.

Durante el desarrollo de la app, quisimos asegurarnos de que tuviera el mejor rendimiento posible. El punto de partida para la optimización fue un informe de Lighthouse.

Informe de Lighthouse para la app de Oodles
Figura 3: Informe de Lighthouse para la app de Oodles

El rendimiento inicial de nuestra app, como se ve en el informe de Lighthouse, fue bastante terrible. En una red 3G, el usuario debía esperar 15 segundos para obtener la primera pintura significativa o para que la app fuera interactiva. Lighthouse destacó muchos problemas con nuestro sitio, y la puntuación de rendimiento general de 23 reflejó exactamente eso.

El peso de la página era de aproximadamente 3.4 MB. De manera urgente, necesitábamos eliminar un poco de grasa.

Esto inició nuestro primer desafío de rendimiento: encontrar elementos que podamos quitar con facilidad sin afectar la experiencia general.

Oportunidades de optimización del rendimiento

Quita los recursos innecesarios

Hay algunos elementos obvios que se pueden quitar de forma segura: espacios en blanco y comentarios.

Ganancias de la reducción
Figura 4: Minifica y comprime JavaScript y CSS

Lighthouse destaca esta oportunidad en la auditoría de CSS y JavaScript nominificados. Estábamos usando webpack para nuestro proceso de compilación, por lo que, para reducir la escala, simplemente usamos el complemento Uglify JS.

La reducción es una tarea común, por lo que debes poder encontrar una solución prediseñada para cualquier proceso de compilación que utilices.

Otra auditoría útil en ese espacio es Habilitar la compresión de texto. No hay motivo para enviar archivos sin comprimir y, en la actualidad, la mayoría de los CDN son compatibles con esta opción.

Usamos Firebase Hosting para alojar nuestro código, y Firebase habilita gzipping de forma predeterminada. Por eso, debido a la virtud de alojar nuestro código en una CDN razonable, obtuvimos gratis eso.

Si bien gzip es una forma muy popular de compresión, otros mecanismos como Zopfli y Brotli también están obteniendo terreno. Brotli es compatible con la mayoría de los navegadores, y puedes usar un objeto binario para comprimir previamente tus elementos antes de enviarlos al servidor.

Usa políticas de caché eficientes

El siguiente paso fue asegurarnos de no enviar recursos dos veces si es innecesario.

La auditoría de la política de caché ineficiente de Lighthouse nos ayudó a darnos cuenta de que podríamos optimizar nuestras estrategias de almacenamiento en caché para lograr exactamente eso. Al configurar un encabezado de vencimiento de max-age en nuestro servidor, nos aseguramos de que, en una visita repetida, el usuario pueda reutilizar los recursos que descargó antes.

Lo ideal es que te enfoques en almacenar en caché tantos recursos de la manera más segura posible durante el mayor período posible y proporcionar tokens de validación para una revalidación eficiente de los recursos que se actualizaron.

Quita el código sin usar

Hasta ahora, quitamos las partes obvias de la descarga innecesaria, pero ¿qué pasa con las partes menos obvias? Por ejemplo, código sin usar.

Cobertura de código en Herramientas para desarrolladores
Figura 5: Verifica la cobertura de código

A veces, incluimos en el código de nuestras apps que no es realmente necesario. Esto sucede en especial si trabajas en la app durante un período más largo, tu equipo o tus dependencias cambian y, a veces, una biblioteca huérfana se retrasa. Eso fue exactamente lo que nos pasó.

Al principio, usábamos la biblioteca de Componentes de Material para prototipar nuestra app rápidamente. Con el tiempo, pasamos a una apariencia más personalizada y nos olvidamos por completo de esa biblioteca. Afortunadamente, la verificación de cobertura de código nos ayudó a redescubrirla en nuestro paquete.

Puedes verificar las estadísticas de cobertura de código en Herramientas para desarrolladores, tanto para el tiempo de ejecución como para el tiempo de carga de tu aplicación. Puedes ver las dos franjas rojas grandes en la captura de pantalla inferior. Teníamos más del 95% de nuestro CSS sin usar y también mucho JavaScript.

Lighthouse también detectó este problema en la auditoría de reglas de CSS sin usar. Mostró un ahorro potencial de más de 400 KB. Volvimos a nuestro código y quitamos la parte de JavaScript y CSS de esa biblioteca.

Si dejamos caer el adaptador MVC, nuestros estilos se reducirán a 10 KB.
Figura 6: Si colocamos el adaptador MVC, nuestros estilos se reducirán a 10 KB.

Esto redujo 20 veces nuestro paquete de CSS, lo que es bastante bueno para una confirmación pequeña de dos líneas.

Por supuesto, hizo que nuestra puntuación de rendimiento aumentara, y también el tiempo de carga mejoró significativamente.

Sin embargo, con este tipo de cambios, no basta con verificar solo las métricas y puntuaciones. Quitar el código real nunca es una actividad libre de riesgos, por lo que siempre debes prestar atención a posibles regresiones.

Nuestro código no se usó en el 95%, pero queda este 5% en alguna parte. Al parecer, uno de nuestros componentes todavía usaba los estilos de esa biblioteca: las pequeñas flechas del control deslizante de doodle. Sin embargo, como era tan pequeño, podíamos incorporar esos estilos de forma manual en los botones.

Se rompieron los botones debido a la biblioteca faltante
Figura 7: Uno de los componentes todavía usaba la biblioteca que se quitó

Por lo tanto, si quitas código, asegúrate de contar con un flujo de trabajo de prueba adecuado para protegerte contra posibles regresiones visuales.

Evita cargas útiles de red de gran tamaño

Sabemos que los recursos de gran tamaño pueden ralentizar la carga de las páginas web. Pueden costarles dinero a los usuarios y pueden tener un gran impacto en sus planes de datos, por lo que es muy importante tener esto en cuenta.

Lighthouse pudo detectar un problema con algunas de las cargas útiles de red mediante la auditoría Carga útil de red enorme.

Detecta cargas útiles de red de gran tamaño
Figura 8: Detecta cargas útiles de red de gran tamaño

Aquí vimos que teníamos más de 3 MB de código que se enviaban, lo cual es bastante, especialmente en dispositivos móviles.

En la parte superior de esta lista, Lighthouse destacó que teníamos un paquete de proveedores de JavaScript que tenía 2 MB de código sin comprimir. Este también es un problema destacado por webpack.

Como dice el dicho, la solicitud más rápida es la que no se realiza.

Lo ideal es medir el valor de cada recurso que ofreces a los usuarios, medir el rendimiento de esos recursos y decidir si vale la pena enviarlo con la experiencia inicial. A veces, estos recursos se pueden aplazar, cargarse de forma diferida o procesar durante el tiempo de inactividad.

En este caso, dado que estamos trabajando con muchos paquetes de JavaScript, tuvimos la fortuna de contar con la suerte porque la comunidad de JavaScript cuenta con un amplio conjunto de herramientas de auditoría de paquetes de JavaScript.

Auditoría de paquete de JavaScript
Figura 9: Auditoría de paquete de JavaScript

Comenzamos con el analizador de paquetes de webpack, que nos informó que estábamos incluyendo una dependencia llamada unicode que era de 1.6 MB de JavaScript analizado, por lo que es bastante.

Luego, volvimos a nuestro editor y, con el complemento de costo de importación para código visual, pudimos visualizar el costo de cada módulo que estábamos importando. Esto nos permitió descubrir qué componente incluía el código que hacía referencia a este módulo.

Luego, cambiamos a otra herramienta, BundlePhobia. Esta es una herramienta que te permite ingresar el nombre de cualquier paquete de NPM y ver cuál se estima su tamaño reducido y comprimido en gzip. Encontramos una buena alternativa para el módulo de slug que usamos, que solo pesaba 2.2 KB, y la cambiamos.

Esto tuvo un gran impacto en nuestro rendimiento. Gracias a este cambio y al descubrir otras oportunidades para reducir el tamaño de nuestro paquete de JavaScript, ahorramos 2.1 MB de código.

Notamos mejoras generales del 65% una vez que se toma en cuenta el tamaño comprimido y comprimido en gzip de estos paquetes. Y descubrimos que realmente valía la pena hacerlo como un proceso.

Por lo general, intenta eliminar las descargas innecesarias en tus sitios y aplicaciones. Hacer un inventario de los recursos y medir su impacto en el rendimiento puede marcar una gran diferencia, así que asegúrate de auditarlos con bastante frecuencia.

Menor tiempo de inicio de JavaScript con división de código

Si bien las cargas útiles de red de gran tamaño pueden tener un gran impacto en nuestra app, hay otro aspecto que puede tener un gran impacto: JavaScript.

JavaScript es tu recurso más costoso. En dispositivos móviles, si envías grandes paquetes de JavaScript, puede retrasar la rapidez con la que los usuarios pueden interactuar con los componentes de la interfaz de usuario. Eso significa que pueden presionar la IU sin que suceda nada significativo. Por ello, es importante que comprendamos por qué JavaScript cuesta tanto.

Así es como un navegador procesa JavaScript.

Procesamiento de JavaScript
Figura 10: Procesamiento de JavaScript

Primero, tenemos que descargar esa secuencia de comandos, tenemos un motor de JavaScript que luego necesita analizar ese código, compilarlo y ejecutarlo.

Estas fases no requieren mucho tiempo en dispositivos de alta gama, como una máquina de escritorio o una laptop, o incluso un teléfono de alta gama. Sin embargo, en un teléfono celular promedio, este proceso puede tardar entre cinco y diez veces más. Esto es lo que retrasa la interactividad, por lo que es importante que tratemos de reducirla.

Para ayudarte a descubrir estos problemas con tu app, presentamos una nueva auditoría de tiempo de inicio de JavaScript en Lighthouse.

Tiempo de inicio de JavaScript
Figura 11: Auditoría de tiempo de inicio de JavaScript

Y en el caso de la app de Oodle, nos dijo que teníamos 1.8 segundos de tiempo en el arranque de JavaScript. Lo que sucedía era que estábamos importando de forma estática en todas nuestras rutas y componentes a un paquete monolítico de JavaScript.

Una técnica para solucionar esto es usar la división de código.

Dividir el código es como pizza

La división de código es esta noción de brindar a los usuarios todo lo que necesitan de JavaScript.

La división de código se puede aplicar a nivel de ruta o de componentes. Funciona muy bien con React y React Loadable, Vue.js, Angular, Polymer, Preact y muchas otras bibliotecas.

Incorporamos la división de código en nuestra aplicación y pasamos de las importaciones estáticas a las importaciones dinámicas, lo que nos permite cargar código de forma asíncrona según sea necesario.

División de código con importaciones dinámicas
Figura 13: División de código con importaciones dinámicas

El impacto que esto tuvo fue tanto la reducción del tamaño de nuestros paquetes como la disminución del tiempo de inicio de JavaScript. La app disminuyó a 0.78 segundos, lo que hizo que la app fuera un 56% más rápida.

En general, si estás compilando una experiencia con mucho JavaScript, asegúrate de enviar solo código al usuario que lo necesita.

Aprovecha conceptos como la división de código, explora ideas como la eliminación de código no utilizado y consulta el repositorio webpack-libs-optimizations para obtener algunas ideas sobre cómo reducir el tamaño de tu biblioteca si usas webpack.

Optimiza imágenes

Broma del rendimiento de carga de imágenes

En la app de Oodle, estamos usando muchas imágenes. Por desgracia, Lighthouse no tenía tanto entusiasmo que nosotros. De hecho, no logramos realizar las tres auditorías relacionadas con las imágenes.

Olvidamos optimizar nuestras imágenes; no las estábamos ajustando el tamaño correctamente y, además, pudimos obtener algunas ventajas si usamos otros formatos de imagen.

Auditorías de imágenes
Figura 14: Auditorías de imágenes de Lighthouse

Comenzamos a optimizar nuestras imágenes.

Para una ronda de optimización única, puedes usar herramientas visuales como ImageOptim o XNConvert.

Un enfoque más automatizado consiste en agregar un paso de optimización de imágenes a tu proceso de compilación con bibliotecas como imagemin.

De esta manera, te aseguras de que las imágenes que se agreguen en el futuro se optimicen automáticamente. Algunas CDN, por ejemplo, Akamai o soluciones de terceros como Cloudinary, Fastly o Uploadcare te ofrecen soluciones integrales de optimización de imágenes, por lo que también puedes alojar tus imágenes en esos servicios.

Si no quieres hacerlo debido al costo o a los problemas de latencia, los proyectos como Thumbor o Imageflow ofrecen alternativas autoalojadas.

Antes y después de la optimización
Figura 15: Antes y después de la optimización

El PNG de fondo se marcó en webpack como grande, y con razón. Después de ajustar su tamaño correctamente en el viewport y ejecutarlo mediante ImageOptim, bajamos a 100 KB, lo cual es aceptable.

Si repites este proceso para varias imágenes de nuestro sitio, podemos reducir considerablemente el peso total de la página.

Usa el formato correcto para el contenido animado

Los GIFs pueden ser muy costosos. Sorprendentemente, fue que el formato GIF nunca fue concebido como una plataforma de animación en primer lugar. Por lo tanto, cambiar a un formato de video más adecuado te ofrece grandes ahorros en términos de tamaño de archivo.

En la app de Oodle, usamos un GIF como secuencia de introducción en la página principal. Según Lighthouse, podríamos ahorrar más de 7 MB si cambiamos a un formato de video más eficiente. Nuestro clip pesaba alrededor de 7.3 MB, lo que es demasiado para cualquier sitio web razonable, así que lo convertimos en un elemento de video con dos archivos fuente: mp4 y WebM para una mayor compatibilidad con los navegadores.

Cómo reemplazar GIF animados por video
Figura 16: Reemplaza los GIF animados por videos

Usamos la herramienta FFmpeg para convertir el GIF de animación en un archivo mp4. El formato WebM ofrece ahorros aún mayores: la API de ImageOptim puede realizar la conversión por ti.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

Logramos ahorrar más del 80% de nuestro peso general gracias a esta conversión. Esto nos llevó a alrededor de 1 MB.

Aun así, 1 MB es un gran recurso para seguir el proceso, en especial para un usuario con un ancho de banda restringido. Afortunadamente, podríamos usar la API de Effective Type para darnos cuenta de que usan un ancho de banda lento y darles un JPEG mucho más pequeño en su lugar.

Esta interfaz usa el tiempo de ida y vuelta efectivo y los valores de reducción para estimar el tipo de red que está utilizando el usuario. Simplemente muestra una cadena, 2G, 2G, 3G o 4G lento. Por lo tanto, según este valor, si el usuario está en menos de 4G, podríamos reemplazar el elemento de video con la imagen.

if (navigator.connection.effectiveType) { ... }

Quita un poco la experiencia, pero al menos el sitio se puede usar con una conexión lenta.

Imágenes fuera de pantalla de carga diferida

Los carruseles, los controles deslizantes o las páginas muy largas suelen cargar imágenes, aunque el usuario no puede verlas de inmediato en la página.

Lighthouse marcará este comportamiento en la auditoría de imágenes fuera de la pantalla, y también podrás verlo en el panel de red de Herramientas para desarrolladores. Si ves muchas imágenes entrantes y solo algunas están visibles en la página, significa que podrías considerar la carga diferida en su lugar.

La carga diferida aún no es compatible de forma nativa en el navegador, por lo que debemos usar JavaScript para agregar esta capacidad. Usamos la biblioteca Lazysizes para agregar un comportamiento de carga diferida a las portadas de Oodle.

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

Lazysizes es inteligente porque no solo hace un seguimiento de los cambios de visibilidad del elemento, sino que también carga previamente de manera proactiva los elementos que están cerca de la vista para ofrecer una experiencia del usuario óptima. También ofrece una integración opcional de IntersectionObserver, que te brinda búsquedas de visibilidad muy eficientes.

Después de este cambio, nuestras imágenes se recuperarán según demanda. Si deseas profundizar en ese tema, consulta images.guide, un recurso muy útil y completo.

Ayuda al navegador a entregar los recursos críticos de manera anticipada

No todos los bytes que se envían por cable tienen el mismo grado de importancia, y el navegador lo sabe. Muchos navegadores tienen una heurística para decidir qué deben recuperar primero. En ocasiones, recuperan CSS antes que imágenes o secuencias de comandos.

Nos puede resultar útil que nosotros, como autores de la página, informemos al navegador lo que realmente nos importa. Afortunadamente, durante los últimos años, los proveedores de navegadores han agregado varias funciones para ayudarnos con esto, p.ej., sugerencias de recursos como link rel=preconnect, preload o prefetch.

Estas capacidades que se incorporaron a la plataforma web ayudan al navegador a recuperar lo correcto en el momento adecuado y pueden ser un poco más eficientes que algunos de los enfoques personalizados basados en la lógica y la carga que se realizan con secuencias de comandos.

Veamos cómo Lighthouse nos guía para usar algunas de estas funciones de manera efectiva.

Lo primero que Lighthouse nos pide que hagamos es evitar los costosos viajes de ida y vuelta a cualquier origen.

Evita los viajes de ida y vuelta costosos y múltiples a cualquier origen
Figura 17: Evita los viajes de ida y vuelta costosos y múltiples a cualquier origen

En el caso de la app de Oodle, en realidad usamos mucho Google Fonts. Cada vez que se suelta una hoja de estilo de Google Fonts en tu página, se conectarán hasta dos subdominios. Lighthouse nos dice que, si logramos preparar esa conexión, podríamos ahorrarnos hasta 300 milisegundos en el tiempo de conexión inicial.

Con la conexión previa de vínculo rel, podemos enmascarar esa latencia de conexión de forma eficaz.

En particular, con elementos como Google Fonts, en el que nuestro CSS de tipo de fuente se aloja en googleapis.com y nuestros recursos de fuentes se alojan en Gstatic, esto puede tener un gran impacto. Así que aplicamos esta optimización y reducimos unos cientos de milisegundos.

Lo siguiente que Lighthouse sugiere es que precarguemos las solicitudes clave.

Precarga las solicitudes clave
Figura 18: Precarga las solicitudes de clave

<link rel=preload> es muy potente, ya que informa al navegador que se necesita un recurso como parte de la navegación actual y trata de que el navegador lo recupere lo antes posible.

En este punto, Lighthouse nos dice que debemos ir y precargar nuestros recursos clave de fuentes web, porque los estamos cargando en dos fuentes web.

La precarga en una fuente web se ve de la siguiente manera: especifica rel=preload, pasas as con el tipo de fuente y, luego, especificas el tipo de fuente en el que intentas cargarlo, como woff2.

El impacto que esto puede tener en tu página es bastante grave.

Impacto de la precarga de recursos
Figura 19: Impacto de la precarga de recursos

Por lo general, sin usar la precarga de vínculos rel, si las fuentes web son críticas para tu página, lo que el navegador debe hacer es, en primer lugar, recuperar tu HTML, analizar tu CSS y, mucho más adelante, recogerá tus fuentes web.

Con la carga previa de vínculos rel, tan pronto como el navegador analice tu código HTML, podrá comenzar a recuperar esas fuentes web mucho antes. En el caso de nuestra app, esto redujo un segundo el tiempo que nos tomó renderizar texto con nuestras fuentes web.

No es tan sencillo si vas a intentar precargar fuentes con Google Fonts, hay un inconveniente.

Las URL de Google Font que especificamos en los tipos de fuente en las hojas de estilo eran algo que el equipo de fuentes actualizaba con bastante frecuencia. Estas URLs pueden vencer o actualizarse con frecuencia, por lo que te sugerimos que hagas un control total de la experiencia de carga de fuentes, que aloja tus fuentes web por tu cuenta. Esto puede ser muy útil porque te da acceso a funciones como la carga previa de vínculos rel.

En nuestro caso, la herramienta Google Web Fonts Helper nos ayudó a desconectar algunas de esas fuentes web y a configurarlas localmente. Por lo tanto, revísala.

Ya sea que uses fuentes web como parte de tus recursos esenciales o que sean JavaScript, intenta ayudar al navegador a entregar los recursos esenciales lo antes posible.

Experimental: Sugerencias de prioridad

Tenemos algo especial para compartir contigo hoy. Además de las sugerencias de recursos y la precarga, trabajamos en una nueva función experimental del navegador a la que llamamos "sugerencias de prioridad".

Cómo establecer la prioridad para el contenido visible inicialmente
Figura 20: Sugerencias de prioridad

Esta es una función nueva que permite indicarle al navegador la importancia de un recurso. Expone un atributo nuevo (la importancia) con valores bajos, altos o automáticos.

Esto nos permite transmitir la disminución de la prioridad de los recursos menos importantes, como los diseños no críticos, las imágenes o las llamadas a la API de recuperación para reducir la contención. También podemos impulsar la prioridad de cosas más importantes, como nuestras hero images.

En el caso de nuestra app de Oodle, esto nos llevó a un lugar práctico donde podíamos optimizar.

Cómo establecer la prioridad para el contenido visible inicialmente
Figura 21: Establece la prioridad para el contenido visible inicialmente

Antes de agregar la carga diferida a nuestras imágenes, lo que hacía el navegador era el carrusel de imágenes con todos nuestros dibujos, y el navegador recuperaba todas las imágenes desde el principio del carrusel con una prioridad alta. Lamentablemente, las imágenes del medio del carrusel eran las más importantes para el usuario. Así que lo que hicimos fue establecer la importancia de esas imágenes de fondo en muy baja, de las de primer plano en muy alta, lo que tuvo un impacto de dos segundos con la conexión 3G lenta y la rapidez con la que pudimos recuperar y renderizar esas imágenes. Fue una experiencia muy positiva.

Esperamos implementar esta función en Canary dentro de unas semanas, así que mantente alerta.

Tener una estrategia de carga de fuentes web

La tipografía es fundamental para lograr un buen diseño, y, si usas fuentes web, lo ideal es que no bloquees la renderización de tu texto y, definitivamente, no quieras mostrar texto invisible.

Ahora destacamos esto en Lighthouse con la auditoría de evitar texto invisible mientras se cargan las fuentes web.

Cómo evitar el texto invisible mientras se cargan las fuentes web
Figura 22: Evita el texto invisible mientras se cargan las fuentes web

Si cargas tus fuentes web con un bloque de tipo de fuente, permites que el navegador decida qué hacer si esa fuente web tarda mucho tiempo en recuperarse. Algunos navegadores esperan en cualquier lugar hasta tres segundos antes de recurrir a una fuente del sistema y, finalmente, la cambian a la fuente una vez que se descarga.

Tratamos de evitar este texto invisible, por lo que, en este caso, no habríamos podido ver los garabatos clásicos de esta semana si la fuente web hubiera llevado demasiado tiempo. Afortunadamente, con una función nueva llamada font-display, obtienes mucho más control sobre este proceso.

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

La visualización de fuentes te ayuda a decidir cómo se renderizarán o recurrirán las fuentes web en función del tiempo que tarden en intercambiarse.

En este caso, usamos el intercambio de pantallas. El intercambio le da al tipo de fuente un período de bloqueo de cero segundos y un período de intercambio infinito. Esto significa que el navegador dibujará el texto de inmediato con una fuente de resguardo si la fuente tarda un poco en cargarse. Se cambiará una vez que el tipo de fuente esté disponible.

En el caso de nuestra app, esto fue excelente porque nos permitió mostrar un texto significativo desde el principio y hacer la transición a la fuente web una vez que estaba lista.

Resultado de visualización de la fuente
Figura 23: Resultado de visualización de la fuente

En general, si utilizas fuentes web, como lo hace un gran porcentaje de la Web, implementa una buena estrategia de carga de fuentes web.

Existen muchas funciones de la plataforma web que puedes usar para optimizar tu experiencia de carga de fuentes, pero también puedes consultar el repositorio de Web Font Recipes de Zach Wineman, que es realmente genial.

Reduce las secuencias de comandos que bloquean el procesamiento

Hay otras partes de la aplicación que podríamos incluir antes en la cadena de descargas para proporcionar al menos una experiencia del usuario básica un poco antes.

En la línea de tiempo de Lighthouse, puedes ver que, durante los primeros segundos, cuando se cargan todos los recursos, el usuario no puede ver ningún contenido.

Reduce la oportunidad de las hojas de estilo que bloquean la renderización
Figura 24: Reduce la oportunidad de las hojas de estilo que bloquean el procesamiento

La descarga y el procesamiento de hojas de estilo externas impiden que nuestro proceso de renderización realice ningún progreso.

Podemos mostrar algunos de los estilos un poco antes para optimizar la ruta de acceso de representación crítica.

Si extraemos los estilos responsables de esta renderización inicial y los intercalamos en nuestro HTML, el navegador podrá renderizarlos de inmediato sin esperar a que lleguen las hojas de estilo externas.

En nuestro caso, usamos un módulo de NPM llamado Critical para intercalar nuestro contenido crítico en index.html durante un paso de compilación.

Si bien este módulo hizo la mayor parte del trabajo pesado, todavía era un poco difícil lograr que funcionara sin problemas en las diferentes rutas.

Si no tienes cuidado o la estructura de tu sitio es muy compleja, puede ser muy difícil introducir este tipo de patrón si no planificaste una arquitectura de shell de app desde el principio.

Por eso es tan importante tener en cuenta las consideraciones sobre el rendimiento desde el principio. Si no diseñas para lograr el rendimiento desde el principio, es muy probable que tengas problemas al hacerlo más adelante.

Al final, nuestro riesgo dio frutos, logramos que funcionara, y la app comenzó a entregar contenido mucho antes, lo que mejoró significativamente nuestro primer tiempo de procesamiento de imagen significativo.

Resultado

Esa fue una larga lista de optimizaciones de rendimiento que aplicamos a nuestro sitio. Echemos un vistazo al resultado. Así es como se cargó nuestra app en un dispositivo móvil mediano en una red 3G, antes y después de la optimización.

La puntuación de rendimiento de Lighthouse subió de 23 a 91. Ese es un progreso bastante agradable en términos de velocidad. Verificamos y seguimos el informe de Lighthouse de forma continua para impulsar todos los cambios. Si quieres ver cómo implementamos técnicamente todas las mejoras, no dudes en consultar nuestro repositorio, en especial, las preguntas de contacto que llegaron allí.

Rendimiento predictivo: Experiencias del usuario basadas en datos

Creemos que el aprendizaje automático representa una oportunidad emocionante para el futuro en muchas áreas. Una idea que esperamos que genere más experimentación en el futuro es que los datos reales realmente pueden guiar las experiencias del usuario que creamos.

En la actualidad, tomamos muchas decisiones arbitrarias sobre lo que el usuario puede querer o necesitar y, por lo tanto, qué vale la pena cargar previamente, precargar o almacenar en caché. Si suponemos que tenemos razón, podemos priorizar una pequeña cantidad de recursos, pero es realmente difícil escalarla a todo el sitio web.

De hecho, actualmente, tenemos datos disponibles para fundamentar mejor nuestras optimizaciones. Con la API de informes de Google Analytics, podemos ver la siguiente página principal y los porcentajes de salida de cualquier URL de nuestro sitio y, por lo tanto, sacar conclusiones sobre qué recursos deberíamos priorizar.

Si combinamos esto con un buen modelo de probabilidad, evitamos desperdiciar los datos de nuestros usuarios cargando en exceso el contenido de forma drástica. Podemos aprovechar esos datos de Google Analytics y usar aprendizaje automático y modelos como las cadenas de Markov o la red neuronal para implementar esos modelos.

Paquete basado en datos para apps web
Figura 25: Paquete basado en datos para apps web

Para facilitar estos experimentos, nos complace anunciar una nueva iniciativa que llamaremos Guess.js.

Guess.js
Figura 26: Guess.js

Guess.js es un proyecto centrado en experiencias del usuario basadas en datos para la Web. Esperamos que inspire a explorar el uso de datos para mejorar el rendimiento web e ir más allá. Todo es de código abierto y ya está disponible en GitHub. Esto se creó en colaboración con la comunidad de código abierto de Minko Gechev, Kyle Matthews de Gatsby, Katie Hempenius y varios más.

Visita Guess.js y danos tu opinión.

Resumen

Las puntuaciones y las métricas son útiles para mejorar la velocidad de la Web, pero son solo los medios, no los objetivos en sí.

Todos hemos experimentado cargas de páginas lentas sobre la marcha, pero ahora tenemos la oportunidad de ofrecerles a nuestros usuarios experiencias más agradables que se cargan muy rápido.

Mejorar el rendimiento es un recorrido. Realizar muchos cambios pequeños pueden generar grandes ganancias. Si usas las herramientas de optimización adecuadas y prestas atención a los informes de Lighthouse, puedes proporcionar una experiencia mejor y más inclusiva a tus usuarios.

Queremos dar un agradecimiento especial a Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse y Doodles de Google.