Web Performance Easy: Google I/O edición 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í las explicamos con la app de 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 bastante ocupados tratando de descubrir cómo hacer que la Web sea más rápida y más eficiente. 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 de 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 mediana 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 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 califican la velocidad como la parte superior de la jerarquía de UX de sus necesidades. Esto no es demasiado 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? (Speed Matters, Vol. 3)

Sabemos que el rendimiento es importante para los usuarios, pero también puede ser un secreto descubrir dónde comenzar a optimizarlo. Afortunadamente, existen herramientas que pueden ayudarte en el proceso.

Lighthouse: Una base para el flujo de trabajo de rendimiento

Lighthouse es una 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 varias 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

Veamos 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 Doodles interactivos de Google favoritos y hasta jugar uno o dos juegos.

Mientras compilamos 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 malo. En una red 3G, el usuario debía esperar 15 segundos para que se realizara la primera renderización significativa 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 el tamaño.

Esto inició 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 recursos innecesarios

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

Beneficios de la reducción
Fig. 4. Reduce el uso de JavaScript y CSS, y comprime estos archivos

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 cualquier proceso de compilación que uses.

Otra auditoría útil en ese espacio es Habilitar la compresión de texto. No hay motivo para enviar archivos sin comprimir, y la mayoría de las CDN admiten esta función 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, gracias a 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 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 caché ineficiente en Lighthouse nos ayudó a notar que podríamos optimizar nuestras estrategias de almacenamiento en caché para lograr exactamente eso. Cuando configuramos un encabezado de vencimiento de max-age en nuestro servidor, nos aseguramos de que, en una visita repetida, el usuario pueda volver a usar los recursos que descargó antes.

Idealmente, debes intentar almacenar en caché la mayor cantidad de recursos posible de la forma más segura durante el período más extenso posible y proporcionar tokens de validación para la revalidación eficiente de 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é sucede con las partes menos obvias? Por ejemplo, código sin usar.

Cobertura de código en DevTools
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 cambia tu equipo o tus dependencias, y, a veces, si se deja 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, cambiamos a un aspecto más personalizado y nos olvidamos por completo de esa biblioteca. Por suerte, la verificación de cobertura de código nos ayudó a volver a descubrirlo 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. Puedes ver las dos grandes franjas rojas en la captura de pantalla inferior. Teníamos más del 95% de nuestro CSS sin usar y una gran cantidad de JavaScript.

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

Si quitamos el adaptador de MVC, nuestros estilos bajan a 10 KB.
Fig. 6: Si quitamos el adaptador de MVC, nuestros estilos bajan a 10 KB.

Esto redujo nuestro paquete de CSS en 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 es suficiente con revisar solo tus métricas y puntuaciones. Quitar el código real nunca es seguro, por lo que siempre debes estar atento a posibles regresiones.

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

Los botones se dañaron porque falta la biblioteca
Fig. 7: Un componente aún usaba la biblioteca quitada.

Por lo tanto, si quitas código, asegúrate de tener un flujo de trabajo de prueba adecuado para ayudarte a 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 costar 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 con la auditoría de 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, especialmente en dispositivos móviles.

En la parte superior de esta lista, Lighthouse destacó que teníamos un paquete de proveedores de JavaScript que era de 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 publicas a tus usuarios, midas el rendimiento de esos recursos y tomes una decisión sobre si vale la pena enviarlos con la experiencia inicial. Porque, a veces, estos recursos se pueden aplazar, cargar de forma diferida o procesar durante el tiempo inactivo.

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

Auditoría de paquetes de JavaScript
Figura 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 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 de reducción y compresión gzip. Encontramos una buena alternativa para el módulo de slug que usábamos, 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 y reducido 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 auditarlos con bastante frecuencia.

Reduce el tiempo de inicio de JavaScript con el fraccionamiento 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 ese es JavaScript.

JavaScript es tu activo más costoso. En dispositivos móviles, si envías paquetes grandes de JavaScript, se puede retrasar la rapidez con la que los usuarios pueden interactuar con los componentes de tu 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

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

Estas fases no llevan 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 retrasa la interactividad, por lo que es importante que intentemos reducirla.

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 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 de código.

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

La división de código es 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 permitió cargar código de forma diferida de manera asíncrona a medida que lo necesitábamos.

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

El impacto que tuvo esto fue reducir el tamaño de nuestros paquetes y disminuir 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 compilando 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 reducción de árbol 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

Broma sobre el rendimiento de la carga de imágenes

En la app de Oodle, usamos muchas imágenes. Lamentablemente, Lighthouse fue mucho menos entusiasta que nosotros. De hecho, no aprobamos las tres auditorías relacionadas con las imágenes.

Nos olvidamos de optimizar nuestras imágenes, no les asignábamos el tamaño correcto y también podríamos obtener algunos beneficios si usáramos otros formatos de imagen.

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

Comenzamos con la optimización de 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, como Akamai o soluciones de terceros como Cloudinary, Fastly o Uploadcare, 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
Figura 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 el tamaño correctamente al viewport y ejecutarlo a través de ImageOptim, bajamos a 100 KB, lo cual es aceptable.

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

Usa el formato correcto para el contenido animado

Los GIFs pueden ser muy costosos. Sorprendentemente, el formato GIF nunca se diseñó 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 compatibilidad más amplia con los navegadores.

Reemplazo de GIF animados por video
Figura 16: Reemplazo de GIF animados por video

Usamos la herramienta FFmpeg para convertir nuestro GIF de animación 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 llevó a alrededor de 1 MB.

Sin embargo, 1 MB es un recurso grande para enviar por cable, en especial para un usuario con un ancho de banda limitado. Por suerte, podemos usar la API de Effective Type para darnos cuenta de que tienen una velocidad de banda lenta y, en su lugar, ofrecerles un JPEG mucho más pequeño.

Esta interfaz usa el tiempo de ida y vuelta efectivo y los valores de bajada para estimar el tipo de red que usa el usuario. Simplemente muestra una cadena, 2G lento, 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) { ... }

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 pueda verlas en la página de inmediato.

Lighthouse marcará este comportamiento en la auditoría de imágenes fuera de la pantalla, y también puedes verlo en el panel de red de DevTools. Si ves muchas imágenes entrantes mientras solo se ven algunas en la página, significa que tal vez deberías considerar la carga diferida.

La carga diferida aún no es compatible de forma nativa en el navegador, por lo que debemos usar JavaScript para agregar esta función. 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 realiza un seguimiento de los cambios de visibilidad del elemento, sino que también carga de forma previa de forma proactiva los elementos que están cerca de la vista para lograr 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 enviados al navegador tienen el mismo nivel 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 el CSS antes que las imágenes o las secuencias de comandos.

Algo que podría ser útil es que nosotros, como autores de la página, informemos al navegador lo que es realmente importante para nosotros. 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 capacidades 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 de carga 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 dice Lighthouse es que evitemos realizar varios viajes de ida y vuelta costosos a cualquier origen.

Evitar varios viajes de ida y vuelta costosos a cualquier origen
Figura 17: Evitar múltiples viajes de ida y vuelta costosos a cualquier origen

En el caso de la app de Oodle, usamos mucho Google Fonts. Cada vez que coloques un hoja de estilo de Google Fonts en tu página, se conectarán hasta dos subdominios. Y lo que Lighthouse nos dice es que, si pudiéramos activar esa conexión, podríamos ahorrar hasta 300 milisegundos en nuestro 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 impacto muy grande, especialmente con algo como Google Fonts, en el que nuestro CSS de fuente está alojado en googleapis.com y nuestros recursos de fuente están alojados en Gstatic. Así que aplicamos esta optimización y ahorramos unos cientos de milisegundos.

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

Carga previamente las 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 debemos precargar nuestros recursos clave de fuentes web, ya que estamos cargando dos fuentes web.

La carga previa en 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 claro.

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

Por lo general, sin usar la carga previa de vínculos rel, si las fuentes web son fundamentales 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, finalmente, recuperar tus fuentes web.

Con la precarga de vínculo rel, en cuanto el navegador analiza tu código HTML, puede comenzar a recuperar esas fuentes web mucho antes. En el caso de nuestra app, esto pudo reducir un segundo el tiempo que tardamos en renderizar texto con nuestras fuentes web.

Ahora bien, si quieres precargar fuentes con Google Fonts, no es tan sencillo, ya que hay un inconveniente.

Las URLs de Google Fonts que especificamos en nuestros tipos de letra en nuestros diseños de hojas de estilo son algo que el equipo de fuentes actualiza con bastante frecuencia. Estas URLs pueden vencer o actualizarse con frecuencia, por lo que te sugerimos que, si deseas tener un control total sobre la experiencia de carga de fuentes, alojes tus fuentes web por tu cuenta. Esto puede ser muy útil, ya que te brinda acceso a elementos como la carga previa de vínculos de rel.

En nuestro caso, la herramienta Google Web Fonts Helper nos resultó muy útil para usar sin conexión algunas de esas fuentes web y configurarlas de forma local, así que pruébala.

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

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

Establece la prioridad del 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: importancia, con los valores bajo, alto o automático.

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

En el caso de nuestra app de Oodle, esto nos llevó a un lugar práctico en el que pudimos 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, el navegador hacía lo siguiente: teníamos este carrusel de imágenes con todos nuestros garabatos, y el navegador recuperaba todas las imágenes al principio del carrusel con una prioridad alta al principio. Lamentablemente, las imágenes del medio del carrusel eran las más importantes para el usuario. Lo que hicimos fue establecer la importancia de esas imágenes de fondo en muy baja y la de primer plano en muy alta, lo que tuvo un impacto de dos segundos en la red 3G lenta y la rapidez con la que pudimos recuperar y renderizar esas imágenes. Una experiencia positiva.

Esperamos lanzar esta función en Canary en unas semanas, así que no te la pierdas.

Tener 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 quieras mostrar texto invisible.

Ahora lo destacamos en Lighthouse, con la auditoría Evitar 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 de texto, le permites al navegador decidir qué hacer si esa fuente web tarda mucho en recuperar. 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 podríamos haber visto los garabatos 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 usarán las fuentes web según el tiempo que tarden en intercambiarse.

En este caso, usamos el intercambio de visualización de fuentes. El intercambio le asigna 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á tu texto de inmediato con una fuente de resguardo si esta tarda un poco en cargarse. Y lo cambiará una vez que el tipo de letra esté disponible.

En el caso de nuestra app, esto fue muy útil porque nos permitió mostrar texto significativo desde el principio y realizar 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, debes tener 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 las fuentes, pero también puedes consultar el repositorio de recetas de fuentes web de Zach Leatherman, porque es realmente excelente.

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 barra de cronograma de Lighthouse, puedes ver que, durante estos primeros segundos en los que se cargan todos los recursos, el usuario no puede ver ningún contenido.

Reduce la oportunidad de los diseños de página que bloquean el procesamiento
Fig. 24: Reduce la oportunidad de los diseños de página que bloquean la renderización

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

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

Si extraemos los estilos responsables de esta renderización inicial y los incorporamos 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 incorporar nuestro contenido crítico en index.html durante un paso de compilación.

Si bien este módulo hizo la mayor parte del trabajo por nosotros, fue un poco complicado lograr que funcionara sin problemas en diferentes rutas.

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

Por eso, es muy importante tener en cuenta el rendimiento desde el principio. Si no diseñas para el rendimiento desde el principio, es 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í 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 buen progreso en términos de velocidad. Todos los cambios se generaron a partir de la verificación y el seguimiento continuos del informe de Lighthouse. Si quieres ver cómo implementamos técnicamente todas las mejoras, no dudes en consultar nuestro repositorio, en especial las PR que se publicaron 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.

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 recuperar previamente, cargar previamente o almacenar en caché previamente. Si acertamos, podemos priorizar una pequeña cantidad de recursos, pero es muy difícil escalarlo a todo el sitio web.

En realidad, tenemos datos disponibles para fundamentar mejor nuestras optimizaciones en la actualidad. 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 nuestros usuarios mediante una precodificación 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 la red neuronal para implementarlos.

Agrupación basada en datos para apps web
Fig. 25. Empaquetado basado en datos para apps web

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

Guess.js
Fig. 26. Guess.js

Guess.js es un proyecto enfocado en experiencias del usuario basadas en datos para la Web. Esperamos que inspire la exploración del uso de datos para mejorar el rendimiento web y mucho más. Todo es de código abierto y está disponible en GitHub en la actualidad. Minko Gechev, Kyle Matthews de Gatsby, Katie Hempenius y otras personas más compilaron esta herramienta en colaboración con la comunidad de código abierto.

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 experimentamos cargas lentas de páginas sobre la marcha, pero ahora tenemos la oportunidad de brindarles a nuestros usuarios experiencias más placenteras que se cargan con rapidez.

Mejorar el rendimiento es un proceso. Muchos cambios pequeños pueden generar grandes ganancias. Si usas las herramientas de optimización adecuadas y tienes en cuenta los informes de Lighthouse, puedes brindar una experiencia mejor y más inclusiva a tus usuarios.

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