Rendimiento web simplificado: edición de Google I/O 2018

En Google I/O 2018, presentamos un resumen de herramientas, bibliotecas y técnicas de optimización que facilitan la mejora del rendimiento web. Aquí, los explicamos con la app de Oodles Theater. También hablamos sobre nuestros experimentos con la carga predictiva y la nueva iniciativa de 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 de Guess.js.

La necesidad de rendimiento

Internet se vuelve cada vez más pesado. Si revisamos 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 las imágenes.

El tamaño creciente 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 consideran que la velocidad es el aspecto más importante de la jerarquía de UX de sus necesidades. Esto no es muy sorprendente, ya que no puedes hacer mucho hasta que se termine 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? (Speed Matters, vol. 3)

Sabemos que el rendimiento es importante para los usuarios, pero también puede parecer un secreto descubrir por dónde empezar a optimizar. Por suerte, existen herramientas que pueden ayudarte en el proceso.

Lighthouse: Una base para el flujo de trabajo de rendimiento

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

Hace poco, lanzamos un conjunto 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 aprovecharlas con un ejemplo práctico: la app de Oodles Theater, una pequeña app web de demostración en la que puedes probar algunos de nuestros Google Doodles interactivos favoritos y hasta jugar un par de juegos.

Mientras compilá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, como se ve en el informe de Lighthouse, fue bastante terrible. En una red 3G, el usuario debía esperar 15 segundos para que se produjera el primer renderizado con contenido o para que la app se volviera interactiva. Lighthouse destacó muchos problemas con 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 con urgencia.

Esto dio inicio a nuestro primer desafío de rendimiento: encontrar elementos que pudiéramos quitar fácilmente 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: los espacios en blanco y los comentarios.

Ganancias de la reducción
Fig. 4. Minifica y comprime JavaScript y CSS

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

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

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

Usábamos Firebase Hosting para alojar nuestro código, y Firebase habilita la compresión 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 compresión, otros mecanismos como Zopfli y Brotli también están ganando terreno. Brotli es compatible con la mayoría de los navegadores, y puedes usar un objeto binario para comprimir previamente tus recursos antes de enviarlos al servidor.

Usa políticas de caché eficientes

Nuestro siguiente paso fue asegurarnos de no enviar recursos dos veces si no es necesario.

La auditoría de política de almacenamiento en caché ineficiente en Lighthouse nos ayudó a darnos cuenta de que podíamos optimizar nuestras estrategias de almacenamiento en caché para lograr exactamente eso. Si establecemos un encabezado de vencimiento de edad máxima en nuestro servidor, nos aseguramos de que, en una visita repetida, el usuario pueda volver a usar los recursos que descargó antes.

Lo ideal es que intentes almacenar en caché la mayor cantidad posible de recursos de forma segura durante el período más largo posible y que proporciones tokens de validación para volver a validar de forma eficiente los recursos que se actualizaron.

Quita el código que no se use

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

Cobertura de código en las Herramientas para desarrolladores
Fig. 5. Verifica la cobertura de código

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

Al principio, usábamos la biblioteca de componentes de Material para crear prototipos de nuestra app con rapidez. Con el tiempo, pasamos a un diseño más personalizado y nos olvidamos por completo de esa biblioteca. Afortunadamente, la verificación de la cobertura de código nos ayudó a redescubrirlo en nuestro paquete.

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

Lighthouse también detectó este problema en la auditoría de reglas de CSS sin usar. Se mostró un ahorro potencial de más de 400 KB. Así que volvimos a nuestro código y quitamos las partes 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, aumentó nuestra puntuación de rendimiento y también mejoró mucho el Tiempo de interacción.

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 posibles regresiones.

Nuestro código no se usó en un 95%, pero todavía queda un 5% en algún lugar. Al parecer, uno de nuestros componentes aún usaba los estilos de esa biblioteca: las pequeñas flechas del control deslizante de garabatos. Sin embargo, como era tan pequeño, pudimos volver a incorporar esos estilos de forma manual en los botones.

Los botones se rompieron porque falta una biblioteca
Fig. 7. Un componente todavía usaba la biblioteca quitada.

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

Evita cargas útiles de red enormes

Sabemos que los recursos grandes pueden ralentizar la carga de las páginas web. Pueden costarles dinero a nuestros 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 que tuvimos un problema con algunas de nuestras cargas útiles de red con la auditoría Carga útil de red enorme.

Detecta cargas útiles de red enormes
Fig. 8. Detecta cargas útiles de red enormes

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

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

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

Lo ideal es que midas el valor de cada recurso que entregas a tus usuarios, midas el rendimiento de esos recursos y decidas si vale la pena enviarlos con la experiencia inicial. Esto se debe a que, a veces, estos recursos se pueden diferir, cargar de forma diferida o procesar durante el tiempo de inactividad.

En nuestro caso, como lidiamos con muchos paquetes de JavaScript, tuvimos suerte porque 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, es decir, 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 importábamos. 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 es una herramienta que te permite ingresar el nombre de cualquier paquete de NPM y ver cuál es su tamaño estimado minificado y comprimido con gzip. 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 del 65% en general, una vez que se tiene en cuenta el tamaño comprimido con gzip y minimizado de estos paquetes. Y descubrimos que realmente valía la pena hacerlo como un 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 regularidad.

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 otro elemento que puede tener un impacto muy grande, y es JavaScript.

JavaScript es tu recurso más costoso. En dispositivos móviles, si envías paquetes grandes de JavaScript, se puede retrasar el tiempo en que los usuarios pueden interactuar con los componentes de la interfaz de usuario. Eso significa que pueden presionar la IU sin que ocurra 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

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

Estas fases no tardan 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 con tu app, presentamos una nueva auditoría del tiempo de inicio de JavaScript en 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 se dedicaron 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 de código.

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

La división de código es esta noción de que, en lugar de darles a tus usuarios una pizza entera de JavaScript, ¿qué pasaría si solo les dieras una porción a la vez cuando la necesitaran?

La división de código se puede aplicar a nivel de ruta o de 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, cambiamos de importaciones estáticas a importaciones dinámicas, lo que nos permitió cargar código de forma asíncrona y diferida según lo necesitábamos.

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

Esto redujo el tamaño de nuestros paquetes y el 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 estás creando una experiencia con mucho código JavaScript, asegúrate de enviar solo el código que el usuario necesita.

Aprovecha conceptos como la división de código, explora ideas como la eliminación de código muerto y consulta el repositorio 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 la carga de imágenes

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

Olvidamos optimizar nuestras imágenes, no las dimensionamos correctamente y también podríamos obtener algunos beneficios si usamos otros formatos de imagen.

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

Comenzamos optimizando 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 ajustar su tamaño correctamente al viewport y ejecutarlo a través de ImageOptim, bajamos 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 pensó como una plataforma de animación. 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, 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 de origen: un MP4 y un WebM para una mayor compatibilidad con 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 un archivo MP4. El formato WebM te ofrece un ahorro aún mayor. 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 general. Esto redujo el tamaño a alrededor de 1 MB.

Aun así, 1 MB es un recurso grande para enviar por la red, en especial para un usuario con un ancho de banda restringido. Por suerte, podríamos usar la API de tipo de conexión efectiva para darnos cuenta de que tienen un ancho de banda lento y, en su lugar, darles un JPEG mucho más pequeño.

Esta interfaz usa los valores de tiempo de ida y vuelta y de descarga efectivos 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 en 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 pueda verlas en la página de inmediato.

Lighthouse marcará este comportamiento en la auditoría de imágenes fuera de pantalla, y también puedes verlo por tu cuenta en el panel de red de las Herramientas para desarrolladores. Si ves que se cargan muchas imágenes, pero solo algunas son visibles en la página, tal vez deberías considerar cargarlas de forma diferida.

La carga diferida aún no se admite 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 realiza una carga previa proactiva de 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 on demand. 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 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 recuperarán CSS antes que imágenes o secuencias de comandos.

Algo que podría ser útil es que nosotros, como autores de la página, informemos al navegador qué es lo que realmente nos importa. Por suerte, 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, preload o prefetch.

Estas funciones que se incorporaron a la plataforma web ayudan al navegador a recuperar el elemento correcto en el momento adecuado y pueden ser un poco más eficientes que algunos de los enfoques basados en la lógica de carga personalizada 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 dice Lighthouse que hagamos es evitar varios viajes de ida y vuelta costosos a cualquier origen.

Evita los viajes de ida y vuelta múltiples y costosos a cualquier origen
Fig. 17. Evita los viajes de ida y vuelta múltiples y costosos 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 un máximo de dos subdominios. Y lo que Lighthouse nos dice es que, si pudiéramos calentar esa conexión, podríamos ahorrar hasta 300 milisegundos en nuestro tiempo de conexión inicial.

Si aprovechamos la conexión previa de link rel, podemos enmascarar de manera eficaz esa latencia de conexión.

Esto puede tener un gran impacto, en especial con algo como Google Fonts, en el que nuestro CSS de tipo de letra se aloja en googleapis.com y nuestros recursos de fuentes se alojan en Gstatic. Por lo tanto, aplicamos esta optimización y redujimos unos cientos de milisegundos.

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

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

<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.

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

La carga previa en una fuente web se ve de la siguiente manera: 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 link rel preload, 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, mucho más adelante, finalmente, recuperar tus fuentes web.

Con link rel preload, en cuanto el navegador analiza el HTML, puede comenzar a recuperar esas fuentes web mucho antes. En el caso de nuestra app, esto permitió reducir un segundo el tiempo que tardamos en renderizar el texto con nuestras fuentes web.

Ahora bien, no es tan sencillo si vas a intentar precargar fuentes con Google Fonts, hay un problema.

Las URLs de Google Fonts que especificamos en nuestras 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, si quieres tener un control total sobre la experiencia de carga de fuentes, te sugerimos que alojes tus fuentes web de forma local. Esto puede ser excelente, ya que te da acceso a elementos como link rel preload.

En nuestro caso, la herramienta Google Web Fonts Helper nos resultó muy útil para desconectar algunas de esas fuentes web y configurarlas de forma local, así que échale un vistazo.

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 sugerencias de recursos y la carga previa, estuvimos trabajando en una nueva función experimental del navegador que llamamos sugerencias de prioridad.

Establece la prioridad para el contenido visible inicialmente
Fig. 20. Sugerencias de prioridad

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

Esto nos permite reducir la prioridad de los recursos menos importantes, como los estilos no críticos, las imágenes o las llamadas a la API de recuperación, para reducir la contención. También podemos aumentar la prioridad de los elementos más importantes, como las imágenes hero.

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

Establece la prioridad para el contenido visible inicialmente
Fig. 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, 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 prioridad alta desde el principio. Lamentablemente, las imágenes del medio del carrusel eran las más importantes para el usuario. Por lo tanto, lo que hicimos fue establecer la importancia de esas imágenes de fondo en un nivel muy bajo, las de primer plano en un nivel muy alto, y esto tuvo un impacto de dos segundos en una conexión 3G lenta y en la rapidez con la que pudimos recuperar y renderizar esas imágenes. Así que fue una experiencia positiva.

Esperamos llevar esta función a 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 que no bloquees la renderización del texto y, definitivamente, no muestres 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 web

Si cargas tus fuentes web con un bloque de fuente, dejas que el navegador decida qué hacer si tarda mucho tiempo en recuperar esa fuente web. Algunos navegadores esperarán hasta tres segundos antes de recurrir a una fuente del sistema y, con el tiempo, la cambiarán por la fuente una vez que se descargue.

Queremos 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 o se recurrirá a las fuentes web según el tiempo que tarden en intercambiarse.

En este caso, usaremos font-display: swap. Swap le da a la 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. Y la intercambiará una vez que la fuente esté disponible.

En el caso de nuestra app, esto fue genial porque nos permitió mostrar texto significativo muy pronto y hacer la transición a la fuente web una vez que 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.

Hay muchas funciones de la plataforma web que puedes usar para optimizar la experiencia de carga de las fuentes, pero también consulta el repositorio de recetas de fuentes web de Zach Leatherman, porque es realmente genial.

Reduce las 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 de la línea de tiempo de Lighthouse, puedes ver que, durante estos primeros segundos, cuando se cargan todos los recursos, el usuario no puede ver ningún contenido.

Oportunidad de reducir las hojas de estilo que bloquean la renderización
Fig. 24. Oportunidad de reducir las hojas de estilo que bloquean la renderización

La descarga y el procesamiento de hojas de estilo externas bloquean el proceso de renderización para que no se realice ningún progreso.

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 intercalamos en nuestro HTML, el navegador puede 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 por nosotros, aún fue 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 aplicación 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 para hacerlo 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 First Meaningful Paint.

El resultado

Esa fue una larga lista de optimizaciones de rendimiento que aplicamos a nuestro sitio. Veamos el 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 aumentó de 23 a 91. Ese es un progreso bastante bueno en términos de velocidad. Todos los cambios se realizaron gracias a que revisamos y seguimos continuamente el informe de Lighthouse. Si quieres ver cómo implementamos técnicamente todas las mejoras, no dudes en consultar nuestro repositorio, en especial las solicitudes de extracción que se incluyeron 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 de los usuarios que estamos creando.

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

De hecho, hoy en día tenemos datos disponibles para fundamentar mejor nuestras optimizaciones. Con la API de Google Analytics Reporting, 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 debemos priorizar.

Si combinamos esto con un buen modelo de probabilidad, evitamos desperdiciar los datos de nuestro usuario con una carga previa excesiva y 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.

Empaquetado basado en datos para apps web
Fig. 25. Empaquetado 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 centrado en las experiencias del usuario basadas en datos para la Web. Esperamos que esto inspire la exploración del uso de datos para mejorar el rendimiento web y vaya más allá de eso. Todo es de código abierto y está disponible en GitHub hoy mismo. Esto se creó en colaboración con la comunidad de código abierto por Minko Gechev, Kyle Matthews de Gatsby, Katie Hempenius y muchos otros.

Consulta 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ágina lentas cuando estamos en movimiento, 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. Muchos cambios pequeños pueden generar grandes beneficios. 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.

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