Web Performance Made Easy - Google I/O 2018 edition

En Google I/O 2018, presentamos un resumen de las herramientas, las bibliotecas y las técnicas de optimización que facilitan la mejora del rendimiento web. Aquí te explicamos cómo funcionan con la app de The Oodles Theater. También hablamos sobre nuestros experimentos con la carga predictiva y la nueva iniciativa Guess.js.

Ewa Gasperowicz

Durante el último año, estuvimos muy ocupados tratando de descubrir cómo hacer que la Web sea más rápida y tenga un mejor rendimiento. Esto 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 cuando desarrollamos la app de Oodles Theater. En la segunda parte, hablaremos sobre nuestros experimentos con la carga predictiva y la nueva iniciativa Guess.js.

La necesidad de rendimiento

Internet se vuelve más pesado cada año. Si verificamos el estado de la Web, podemos ver que una página promedio en dispositivos móviles pesa alrededor de 1.5 MB, y la mayor parte de ese peso corresponde a JavaScript y a imágenes.

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

La mayoría de los usuarios califica la velocidad como el factor más importante en la jerarquía de necesidades de la UX. Esto no es sorprendente, ya que no puedes hacer mucho hasta que se termina de cargar una página. No puedes obtener valor de la página ni admirar su estética.

Pirámide de jerarquía de UX
Fig. 1. ¿Qué tan importante es la velocidad para los usuarios? (La velocidad importa, vol. 3)

Sabemos que el rendimiento es importante para los usuarios, pero también puede parecer un secreto descubrir por dónde comenzar a optimizar. Por suerte, 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 y te permite auditar tu sitio web, además de brindarte sugerencias para mejorarlo.

Recientemente, lanzamos una serie de nuevas auditorías de rendimiento que son muy útiles en el flujo de trabajo de desarrollo diario.

Nuevas auditorías de Lighthouse
Fig. 2. Nuevas auditorías de Lighthouse

Exploremos cómo puedes aprovecharlos con un ejemplo práctico: la app de Oodles Theater. Es una pequeña app web de demostración en la que puedes probar algunos de nuestros Google Doodles interactivos favoritos y hasta jugar un juego o dos.

Mientras creábamos la app, queríamos 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
Fig. 3. Informe de Lighthouse para la app de Oodles

El rendimiento inicial de nuestra app, según el informe de Lighthouse, era bastante malo. En una red 3G, el usuario debía esperar 15 segundos para el primer procesamiento de imagen significativo o para que la app se volviera interactiva. Lighthouse destacó muchos problemas en nuestro sitio, y la puntuación general de rendimiento de 23 reflejó exactamente eso.

La página pesaba alrededor de 3.4 MB, por lo que necesitábamos reducir su tamaño de forma urgente.

Así comenzó nuestro primer desafío de rendimiento: encontrar elementos que podamos quitar fácilmente sin afectar la experiencia general.

Oportunidades de optimización del rendimiento

Quita los recursos innecesarios

Hay algunas cosas obvias que se pueden quitar de forma segura: espacios en blanco y comentarios.

Ganancias por reducción
Fig. 4. Reduce y comprime JavaScript y CSS.

Lighthouse destaca esta oportunidad en la auditoría de CSS y JavaScript sin comprimir. Usábamos webpack para nuestro proceso de compilación, por lo que, para obtener la reducción, simplemente usamos el complemento Uglify JS.

La minimización es una tarea común, por lo que deberías poder encontrar una solución lista para usar para cualquier proceso de compilación que utilices.

Otra auditoría útil en ese espacio es Habilitar la compresión de texto. No hay motivos para enviar archivos sin comprimir, y la mayoría de las CDN admiten esto de forma predeterminada en la actualidad.

Usábamos Firebase Hosting para alojar nuestro código, y Firebase habilita la compresión con gzip de forma predeterminada, por lo que, por el simple hecho de alojar nuestro código en una CDN razonable, obtuvimos eso de forma gratuita.

Si bien gzip es una forma muy popular de comprimir, otros mecanismos como Zopfli y Brotli también están ganando popularidad. Brotli es compatible con la mayoría de los navegadores, y puedes usar un archivo binario para precomprimir tus recursos antes de enviarlos al servidor.

Usa políticas de caché eficientes

El siguiente paso fue asegurarnos de no enviar recursos dos veces si no era necesario.

La auditoría Política de caché ineficiente en Lighthouse nos ayudó a notar que podíamos optimizar nuestras estrategias de almacenamiento en caché para lograr exactamente eso. Al establecer un encabezado de vencimiento 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 intentes almacenar en caché la mayor cantidad de recursos de forma segura durante el mayor tiempo posible y proporcionar tokens de validación para la reválidació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é sucede con las partes menos obvias? Por ejemplo, código sin usar.

Cobertura de código en Herramientas para desarrolladores
Fig. 5. Verificar la cobertura del código

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

Al principio, usábamos la biblioteca de componentes de Material para crear rápidamente un prototipo de nuestra app. Con el tiempo, adoptamos 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 redescubrirlo en nuestro paquete.

Puedes consultar las estadísticas de cobertura de código en DevTools, tanto para el tiempo de ejecución como para el tiempo de carga de tu aplicación. En la captura de pantalla inferior, puedes ver las dos grandes franjas rojas: teníamos más del 95% de nuestro CSS sin usar y una gran cantidad de JavaScript también.

Lighthouse también detectó este problema en la auditoría de reglas de CSS no utilizadas. Mostró un posible ahorro de más de 400 KB. Así que volvimos a nuestro código y quitamos la parte de JavaScript y CSS de esa biblioteca.

Si quitamos el adaptador de MVC, nuestros estilos se reducen a 10 KB.
Fig. 6. Si quitamos el adaptador de MVC, nuestros estilos se reducen a 10 KB.

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

Por supuesto, nuestro puntaje de rendimiento aumentó y el Tiempo hasta la interactividad mejoró mucho.

Sin embargo, con cambios como este, no basta con verificar solo tus métricas y puntuaciones. Quitar código real nunca está exento de riesgos, por lo que siempre debes estar atento a las posibles regresiones.

Nuestro código no se usó en el 95% de los casos, pero aún existe ese 5% en algún lugar. Aparentemente, uno de nuestros componentes seguía usando los estilos de esa biblioteca: las pequeñas flechas del control deslizante de Doodle. Sin embargo, como era tan pequeño, pudimos volver a incorporar esos estilos a los botones de forma manual.

Los botones se rompieron por la falta de la biblioteca
Fig. 7. Un componente seguía usando la biblioteca quitada.

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

Evita utilizar cargas útiles de red de gran tamaño

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

Lighthouse pudo detectar que teníamos un problema con algunas de nuestras cargas útiles de red a través de la auditoría Carga útil de red enorme.

Detecta cargas útiles de red de gran tamaño
Fig. 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 enviaba, lo que es bastante, sobre todo en dispositivos móviles.

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

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

Lo ideal sería que midieras el valor de cada recurso que publicas para los usuarios, el rendimiento de esos recursos y que decidieras si vale la pena publicarlos con la experiencia inicial. Esto se debe a que, a veces, estos recursos se pueden diferir o cargar de forma diferida, o bien se pueden procesar durante el tiempo de inactividad.

En nuestro caso, como trabajamos con muchos paquetes de JavaScript, tuvimos la suerte de que la comunidad de JavaScript tiene un amplio conjunto de herramientas de auditoría de paquetes de JavaScript.

Auditoría de paquetes de JavaScript
Fig. 9. Auditoría de paquetes 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 era bastante.

Luego, fuimos a nuestro editor y, con el complemento Import Cost para Visual Studio Code, pudimos visualizar el costo de cada módulo que importamos. Esto nos permitió descubrir qué componente incluía código que hacía referencia a este módulo.

Luego, cambiamos a otra herramienta, BundlePhobia. Esta herramienta te permite ingresar el nombre de cualquier paquete de npm y ver el tamaño estimado que tendrá después de comprimirlo con gzip y reducirlo. Encontramos una buena alternativa para el módulo de slug que estábamos usando, que solo pesaba 2.2 KB, por lo que lo cambiamos.

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

Observamos mejoras generales del 65% una vez que se tiene en cuenta el tamaño comprimido y minimizado de estos paquetes. Y descubrimos que valía la pena hacerlo como proceso.

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

Reduce el tiempo de inicio de JavaScript con la división de código

Si bien las cargas útiles de red grandes pueden tener un gran impacto en nuestra app, hay otra cosa que puede tener un impacto aún mayor: JavaScript.

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

Así es como un navegador procesa JavaScript.

Procesamiento de JavaScript
Fig. 10. Procesamiento de JavaScript

En primer lugar, tenemos que descargar ese código, tenemos un motor de JavaScript que luego necesita analizar ese código, necesita compilarlo y ejecutarlo.

Ahora bien, estas fases no requieren mucho tiempo en un dispositivo de alta gama, como una computadora 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 intentemos reducirlo.

Para ayudarte a descubrir estos problemas en tu app, agregamos una nueva auditoría del tiempo de arranque de JavaScript a Lighthouse.

Tiempo de inicio de JavaScript
Fig. 11. Auditoría del tiempo de inicio de JavaScript

En el caso de la app de Oodle, nos indicó que dedicamos 1.8 segundos al inicio de JavaScript. Lo que sucedía era que importábamos de forma estática todas nuestras rutas y componentes en un paquete monolítico de JavaScript.

Una técnica para solucionar este problema es usar la división del código.

La división del código es como la pizza

La división de código se basa en la idea de que, en lugar de darles a los usuarios una pizza entera de JavaScript, ¿qué pasaría si solo les dieras una porción a la vez cuando la necesiten?

La división de código se puede aplicar a nivel de la ruta o del componente. 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 cambiamos de importaciones estáticas a importaciones dinámicas, lo que nos permite cargar de forma asíncrona el código de manera diferida a medida que lo necesitamos.

División de código con importaciones dinámicas
Fig. 13. División de código con importaciones dinámicas

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

En general, si creas una experiencia que requiere mucho JavaScript, asegúrate de enviar al usuario solo el código que 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 de webpack-libs-optimizations para obtener algunas ideas sobre cómo puedes reducir el tamaño de tu biblioteca si usas webpack.

Optimiza imágenes

Chiste sobre el rendimiento de carga de imágenes

En la app de Oodle, usamos muchas imágenes. Lamentablemente, Lighthouse no se mostró tan entusiasmado como nosotros. De hecho, fallamos en las tres auditorías relacionadas con imágenes.

Olvidamos optimizar nuestras imágenes, no las dimensionamos correctamente y también podríamos obtener algunas ventajas si usáramos otros formatos de imagen.

Auditorías de imágenes
Fig. 14. Auditorías de imágenes de Lighthouse

Comenzamos por optimizar nuestras imágenes.

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

Un enfoque más automatizado es 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 problemas de latencia, proyectos como Thumbor o Imageflow ofrecen alternativas autoalojadas.

Antes y después de la optimización
Fig. 15. Antes y después de la optimización

Nuestro PNG de fondo se marcó en webpack como grande, y con razón. Después de ajustarlo correctamente al viewport y ejecutarlo a través de ImageOptim, redujimos su tamaño a 100 kB, lo que es aceptable.

Repetir este proceso para varias imágenes de nuestro sitio nos permitió reducir significativamente el peso general de la página.

Usa el formato adecuado para el contenido animado

Los GIFs pueden ser muy costosos. Sorprendentemente, el formato GIF nunca se concibió como una plataforma de animación. Por lo tanto, cambiar a un formato de video más adecuado te permite ahorrar mucho espacio en el tamaño del archivo.

En la app de Oodle, usábamos 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, demasiado para cualquier sitio web razonable, por lo que lo convertimos en un elemento de video con dos archivos fuente: un archivo MP4 y un archivo WebM para una mayor compatibilidad con los navegadores.

Reemplaza los GIFs animados por videos
Fig. 16. Reemplaza los GIFs animados por videos.

Usamos la herramienta FFmpeg para convertir nuestro GIF animado en el archivo mp4. El formato WebM te ofrece ahorros aún mayores. La API de ImageOptim puede realizar esa conversión por ti.

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

Gracias a esta conversión, logramos ahorrar más del 80% de nuestro peso total. Esto nos permitió reducir el tamaño a alrededor de 1 MB.

Aun así, 1 MB es un recurso grande para enviar por cable, en especial para un usuario con un ancho de banda restringido. Por suerte, pudimos usar la API de Effective Type para darnos cuenta de que tenían un ancho de banda lento y, en su lugar, les proporcionamos un JPEG mucho más pequeño.

Esta interfaz usa los valores efectivos de tiempo de ida y vuelta y de reducción para estimar el tipo de red que usa el usuario. Simplemente devuelve una cadena: slow 2G, 2G, 3G o 4G. Por lo tanto, según este valor, si el usuario tiene una conexión inferior a 4G, podríamos reemplazar el elemento de video por la imagen.

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

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

Carga diferida de imágenes fuera de pantalla

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

Lighthouse marcará este comportamiento en la auditoría de imágenes fuera de pantalla, y también podrás verlo por tu cuenta en el panel de red de DevTools. Si ves que se cargan muchas imágenes, pero solo se muestran algunas en la página, significa que tal vez deberías considerar la posibilidad de cargarlas de forma diferida.

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 el comportamiento de carga diferida a nuestras 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 forma proactiva los elementos que están cerca de la vista para brindar 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 recuperan a pedido. Si quieres profundizar en ese tema, consulta images.guide, un recurso muy útil y completo.

Ayuda al navegador a entregar recursos críticos con anticipación

No todos los bytes que se envían por cable al navegador tienen el mismo grado de importancia, y el navegador lo sabe. Muchos navegadores tienen heurísticas para decidir qué deben recuperar primero. Por lo tanto, a veces recuperan el CSS antes que las imágenes o los scripts.

Algo que podría ser útil es que nosotros, como autores de la página, le informemos al navegador qué es realmente importante para nosotros. Afortunadamente, en los últimos años, los proveedores de navegadores agregaron varias funciones para ayudarnos con esto, p.ej., sugerencias de recursos como link rel=preconnect, o 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 que se realizan con secuencias de comandos.

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

Lo primero que nos indica Lighthouse es que evitemos varios viajes de ida y vuelta costosos a cualquier origen.

Evita viajes de ida y vuelta costosos y múltiples a cualquier origen
Fig. 17. Evita viajes de ida y vuelta costosos y múltiples a cualquier origen.

En el caso de la app de Oodle, usamos mucho Google Fonts. Cada vez que agregues una hoja de estilo de Google Fonts a tu página, se conectará a hasta dos subdominios. Y lo que nos dice Lighthouse es que, si pudiéramos calentar esa conexión, podríamos ahorrar hasta 300 milisegundos en el tiempo de conexión inicial.

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

Esto puede tener un gran impacto, especialmente con algo como Google Fonts, en el que nuestro CSS de la cara de la fuente se aloja en googleapis.com y nuestros recursos de fuentes se alojan en Gstatic. Así que aplicamos esta optimización y redujimos unos cientos de milisegundos.

Lo siguiente que sugiere Lighthouse es que carguemos previamente las solicitudes clave.

Carga previa de solicitudes clave
Fig. 18. Carga previamente las solicitudes de claves

<link rel=preload> es muy potente, ya que le 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.

Aquí, Lighthouse nos indica que deberíamos precargar nuestros recursos de fuentes web clave, ya que estamos cargando dos fuentes web.

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

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

Impacto de la precarga de recursos
Fig. 19. Impacto de la carga previa de recursos

Normalmente, sin usar la precarga de rel de vínculos, si las fuentes web son fundamentales para tu página, lo que el navegador tiene que hacer es, en primer lugar, recuperar tu HTML, analizar tu CSS y, en algún momento mucho más adelante, finalmente irá a recuperar tus fuentes web.

Con la precarga de rel de vínculos, tan pronto como el navegador haya analizado tu HTML, puede comenzar a recuperar esas fuentes web mucho antes. En el caso de nuestra app, esto nos permitió reducir un segundo el tiempo que tardábamos en renderizar texto con nuestras fuentes web.

Ahora bien, no es tan sencillo si vas a intentar cargar previamente fuentes con Google Fonts, ya que hay una trampa.

Las URLs de Google Fonts que especificamos en nuestros rostros de fuentes en nuestras hojas de estilo resultaron ser algo que el equipo de fuentes actualiza con bastante frecuencia. Estas URLs pueden vencer o actualizarse con frecuencia, por lo que te sugerimos que alojes tus fuentes web por tu cuenta si quieres tener un control total sobre la experiencia de carga de fuentes. Esto puede ser muy útil porque te da acceso a funciones como la precarga de rel de vínculos.

En nuestro caso, la herramienta Google Web Fonts Helper nos resultó muy útil para descargar algunas de esas fuentes web y configurarlas de forma local. Te recomendamos que la pruebes.

Ya sea que uses fuentes web como parte de tus recursos críticos o que se trate de JavaScript, intenta ayudar al navegador a entregar tus recursos críticos lo antes posible.

Experimental: Sugerencias de prioridad

Hoy tenemos algo especial para compartir contigo. Además de funciones como las optimizaciones de recursos y la precarga, trabajamos en una nueva función experimental del navegador que llamamos optimizaciones de prioridad.

Establece la prioridad del contenido visible inicialmente
Fig. 20. Sugerencias de prioridad

Esta es una nueva función que te permite sugerirle al navegador la importancia de un recurso. Expone un nuevo atributo (importance) con los valores low, high o auto.

Esto nos permite indicar que se reduce la prioridad de los recursos menos importantes, como los estilos no críticos, las imágenes o las llamadas a la API de Fetch para reducir la contención. También podemos aumentar la prioridad de elementos más importantes, como nuestras imágenes de héroe.

En el caso de nuestra app de Oodle, esto nos llevó a un lugar práctico en el que podíamos realizar optimizaciones.

Establece la prioridad del contenido visible inicialmente
Fig. 21. Establece la prioridad del contenido visible inicialmente.

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

Esperamos lanzar esta función en Canary en unas semanas, así que mantente al tanto.

Ten una estrategia de carga de fuentes web

La tipografía es fundamental para un buen diseño y, si usas fuentes web, lo ideal es no bloquear la renderización del texto y, sin duda, no mostrar texto invisible.

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

Evita el texto invisible mientras se cargan las fuentes web
Fig. 22. Evita el texto invisible mientras se cargan las fuentes para sitios web

Si cargas tus fuentes web con un bloque font-face, permites que el navegador decida qué hacer si la recuperación de esa fuente web tarda mucho. Algunos navegadores esperarán hasta tres segundos antes de recurrir a una fuente del sistema y, finalmente, la reemplazarán por la fuente una vez que se descargue.

Intentamos evitar este texto invisible, por lo que, en este caso, no habríamos podido ver los Doodles clásicos de esta semana si la fuente web hubiera tardado demasiado. Afortunadamente, con una nueva función llamada font-display, tienes 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 las fuentes web o cómo se realizará la copia de seguridad en función del tiempo que tarden en intercambiarse.

En este caso, usamos el intercambio de visualización de fuentes. Swap le otorga a la cara de la fuente un período de bloqueo de cero segundos y un período de intercambio infinito. Esto significa que el navegador dibujará tu texto casi de inmediato con una fuente de resguardo si la fuente tarda en cargarse. Y lo intercambiará una vez que la fuente esté disponible.

En el caso de nuestra app, esto fue genial porque nos permitió mostrar texto significativo desde el principio y hacer la transición a la fuente web cuando estuvo lista.

Resultado de la visualización de la fuente
Fig. 23. Resultado de la visualización de la fuente

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

Existen muchas funciones de la plataforma web que puedes usar para optimizar la experiencia de carga de fuentes, pero también te recomendamos que consultes el repositorio de recetas de fuentes web de Zach Leatherman, ya que es excelente.

Reduce los secuencias de comandos que bloquean la renderización

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

En la tira del cronograma de Lighthouse, puedes ver que, durante esos primeros segundos en los que se cargan todos los recursos, el usuario no puede ver ningún contenido.

Reduce la oportunidad de bloquear la renderización de hojas de estilo
Fig. 24. Oportunidad para reducir las hojas de estilo que bloquean la renderización

La descarga y el procesamiento de hojas de estilo externas impiden que nuestro proceso de renderización avance.

Podemos intentar optimizar nuestra ruta de renderización crítica entregando algunos de los estilos un poco antes.

Si extraemos los estilos responsables de esta renderización inicial y los insertamos en nuestro código 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 insertar 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 por nosotros, aún era un poco difícil lograr que funcionara sin problemas en 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 la arquitectura de shell de la app desde el principio.

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

Al final, nuestro riesgo valió la pena, logramos que funcionara y la app comenzó a entregar contenido mucho antes, lo que mejoró significativamente nuestro tiempo de primera pintura significativa.

El resultado

Esa fue una larga lista de optimizaciones de rendimiento que aplicamos a nuestro sitio. Veamos el resultado. Así 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 aumentó de 23 a 91. Es un progreso bastante bueno en términos de velocidad. Todos los cambios se realizaron porque revisamos y seguimos continuamente el informe de Lighthouse. Si quieres saber cómo implementamos técnicamente todas las mejoras, no dudes en consultar nuestro repo, en especial las PR que se agregaron 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 pueden guiar las experiencias del usuario que creamos.

Actualmente, tomamos muchas decisiones arbitrarias sobre lo que el usuario podría querer o necesitar y, por lo tanto, lo que vale la pena obtener previamente, cargar previamente o almacenar previamente en la caché. Si acertamos, podemos priorizar una pequeña cantidad de recursos, pero es muy difícil ampliarlo a todo el sitio web.

De hecho, hoy tenemos datos disponibles para fundamentar mejor nuestras optimizaciones. Con la API de Google Analytics Reporting, podemos analizar los porcentajes de la siguiente página principal y de salida para cualquier URL de nuestro sitio y, de este modo, extraer conclusiones sobre los recursos a los que debemos dar prioridad.

Si combinamos esto con un buen modelo de probabilidad, evitamos desperdiciar los datos del usuario realizando una recuperación previa agresiva del contenido. Podemos aprovechar esos datos de Google Analytics y usar el aprendizaje automático y modelos como las cadenas de Markov o las redes neuronales para implementar esos modelos.

Agrupamiento basado en datos para apps web
Fig. 25. Agrupamiento basado en datos para apps web

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

Guess.js
Fig. 26. Guess.js

Guess.js es un proyecto que se centra en las experiencias del usuario basadas en datos para la Web. Esperamos que inspire a explorar el uso de datos para mejorar el rendimiento web y mucho más. Todo es de código abierto y está disponible en GitHub hoy mismo. Esta función se creó en colaboración con la comunidad de código abierto por Minko Gechev, Kyle Matthews de Gatsby, Katie Hempenius y muchas otras personas.

Echa un vistazo a Guess.js y dinos qué te parece.

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 lentas de páginas mientras nos desplazamos, pero ahora tenemos la oportunidad de brindarles a nuestros usuarios experiencias más agradables que se carguen muy rápido.

Mejorar el rendimiento es un proceso continuo. 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 brindar una experiencia mejor y más inclusiva a tus usuarios.

Agradecimientos especiales a Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse y Google Doodles.