Rendimiento front-end 2021: optimizaciones de entrega

Publicado: 2022-03-10
Resumen rápido ↬ ¡Hagamos el 2021... rápido! Una lista de verificación anual de rendimiento de front-end con todo lo que necesita saber para crear experiencias rápidas en la web hoy, desde métricas hasta herramientas y técnicas de front-end. Actualizado desde 2016.

Tabla de contenido

  1. Preparándose: planificación y métricas
  2. Establecer metas realistas
  3. Definición del entorno
  4. Optimizaciones de activos
  5. Optimizaciones de compilación
  6. Optimizaciones de entrega
  7. Redes, HTTP/2, HTTP/3
  8. Pruebas y monitoreo
  9. Triunfos rápidos
  10. Todo en una página
  11. Descargar la lista de verificación (PDF, Apple Pages, MS Word)
  12. Suscríbase a nuestro boletín de correo electrónico para no perderse las próximas guías.

Optimizaciones de entrega

  1. ¿ defer para cargar JavaScript crítico de forma asíncrona?
    Cuando el usuario solicita una página, el navegador obtiene el HTML y construye el DOM, luego obtiene el CSS y construye el CSSOM, y luego genera un árbol de representación haciendo coincidir el DOM y el CSSOM. Si es necesario resolver algún JavaScript, el navegador no comenzará a mostrar la página hasta que se resuelva, lo que retrasará el procesamiento. Como desarrolladores, tenemos que decirle explícitamente al navegador que no espere y que comience a mostrar la página. La forma de hacer esto para los scripts es con los atributos defer y async en HTML.

    En la práctica, resulta que es mejor usar defer en lugar de async . Ah, ¿cuál es la diferencia de nuevo? Según Steve Souders, una vez que llegan los scripts async , se ejecutan inmediatamente, tan pronto como el script está listo. Si eso sucede muy rápido, por ejemplo, cuando el script ya está en caché, en realidad puede bloquear el analizador HTML. Con defer , el navegador no ejecuta scripts hasta que se analiza HTML. Entonces, a menos que necesite ejecutar JavaScript antes de comenzar a renderizar, es mejor usar defer . Además, varios archivos asíncronos se ejecutarán en un orden no determinista.

    Vale la pena señalar que existen algunos conceptos erróneos sobre async y defer . Lo que es más importante, async no significa que el código se ejecutará siempre que el script esté listo; significa que se ejecutará siempre que los scripts estén listos y todo el trabajo de sincronización anterior haya finalizado. En palabras de Harry Roberts: "Si coloca una secuencia de comandos async después de las secuencias de comandos de sincronización, su secuencia de comandos async será tan rápida como la secuencia de comandos de sincronización más lenta".

    Además, no se recomienda usar tanto async como defer . Los navegadores modernos son compatibles con ambos, pero siempre que se usen ambos atributos, async siempre ganará.

    Si desea profundizar en más detalles, Milica Mihajlija ha escrito una guía muy detallada sobre cómo construir el DOM más rápido, entrando en los detalles del análisis especulativo, asíncrono y diferido.

  2. Componentes caros de carga diferida con IntersectionObserver y sugerencias de prioridad.
    En general, se recomienda cargar de forma diferida todos los componentes costosos, como JavaScript pesado, videos, iframes, widgets y potencialmente imágenes. La carga diferida nativa ya está disponible para imágenes e iframes con el atributo de loading (solo Chromium). Bajo el capó, este atributo difiere la carga del recurso hasta que alcanza una distancia calculada desde la ventana gráfica.
    <!-- Lazy loading for images, iframes, scripts. Probably for images outside of the viewport. --> <img loading="lazy" ... /> <iframe loading="lazy" ... /> <!-- Prompt an early download of an asset. For critical images, eg hero images. --> <img loading="eager" ... /> <iframe loading="eager" ... />

    Ese umbral depende de algunas cosas, desde el tipo de recurso de imagen que se obtiene hasta el tipo de conexión efectiva. Pero los experimentos realizados con Chrome en Android sugieren que en 4G, el 97,5 % de las imágenes de la parte inferior de la página que se cargan de forma diferida se cargaron por completo dentro de los 10 ms de volverse visibles, por lo que debería ser seguro.

    También podemos usar el atributo de importance ( high o low ) en un elemento <script> , <img> o <link> (solo parpadeo). De hecho, es una excelente manera de quitarle prioridad a las imágenes en los carruseles, así como a volver a priorizar las secuencias de comandos. Sin embargo, a veces es posible que necesitemos un poco más de control granular.

    <!-- When the browser assigns "High" priority to an image, but we don't actually want that. --> <img src="less-important-image.svg" importance="low" ... /> <!-- We want to initiate an early fetch for a resource, but also deprioritize it. --> <link rel="preload" importance="low" href="/script.js" as="script" />

    La forma más eficaz de realizar una carga diferida un poco más sofisticada es usar la API Intersection Observer, que proporciona una forma de observar de forma asincrónica los cambios en la intersección de un elemento de destino con un elemento antepasado o con la ventana gráfica de un documento de nivel superior. Básicamente, debe crear un nuevo objeto IntersectionObserver , que recibe una función de devolución de llamada y un conjunto de opciones. Luego agregamos un objetivo para observar.

    La función de devolución de llamada se ejecuta cuando el objetivo se vuelve visible o invisible, por lo que cuando intercepta la ventana gráfica, puede comenzar a realizar algunas acciones antes de que el elemento se vuelva visible. De hecho, tenemos un control granular sobre cuándo se debe invocar la devolución de llamada del observador, con rootMargin (margen alrededor de la raíz) y threshold (un solo número o una matriz de números que indican a qué porcentaje de la visibilidad del objetivo estamos apuntando).

    Alejandro García Anglada ha publicado un práctico tutorial sobre cómo implementarlo realmente, Rahul Nanwani escribió una publicación detallada sobre la carga diferida de imágenes de primer plano y de fondo, y Google Fundamentals también proporciona un tutorial detallado sobre la carga diferida de imágenes y videos con Intersection Observer.

    ¿Recuerdas las lecturas largas de narración dirigida por el arte con objetos en movimiento y pegajosos? También puede implementar una narración de desplazamientos eficaz con Intersection Observer.

    Comprueba de nuevo qué más podrías cargar de forma diferida. Incluso las cadenas de traducción de carga diferida y los emoji podrían ayudar. Al hacerlo, Mobile Twitter logró lograr una ejecución de JavaScript un 80 % más rápida a partir de la nueva canalización de internacionalización.

    Sin embargo, una advertencia rápida: vale la pena señalar que la carga diferida debería ser una excepción y no la regla. Probablemente no sea razonable cargar de forma diferida cualquier cosa que realmente desee que la gente vea rápidamente, por ejemplo, imágenes de la página del producto, imágenes destacadas o un script necesario para que la navegación principal se vuelva interactiva.

Un ejemplo que muestra un umbral anterior de 3000 px con descargas de 160 KB (izquierda) mientras que el nuevo umbral tiene una cantidad de 1250 px con solo 90 KB de descargas (derecha) que muestra una mejora en el ahorro de datos diferidos de carga de img
En conexiones rápidas (p. ej., 4G), los umbrales de distancia desde la ventana gráfica de Chrome se redujeron recientemente de 3000 px a 1250 px, y en conexiones más lentas (p. ej., 3G), el umbral cambió de 4000 px a 2500 px. (Vista previa grande)
Una ilustración con texto alrededor de un teléfono móvil que muestra la interfaz de usuario de Twitter, que explica las mejoras en las herramientas de las cadenas de traducción de carga diferida
Mediante la carga diferida de las cadenas de traducción, Mobile Twitter logró lograr una ejecución de JavaScript un 80 % más rápida a partir de la nueva canalización de internacionalización. (Crédito de la imagen: Addy Osmani) (Vista previa grande)
  1. Carga imágenes progresivamente.
    Incluso podría llevar la carga diferida al siguiente nivel agregando la carga progresiva de imágenes a sus páginas. De manera similar a Facebook, Pinterest, Medium y Wolt, primero puede cargar imágenes de baja calidad o incluso borrosas, y luego, a medida que la página continúa cargando, reemplazarlas con las versiones de calidad completa utilizando la técnica BlurHash o LQIP (marcadores de posición de imagen de baja calidad) técnica.

    Las opiniones difieren si estas técnicas mejoran la experiencia del usuario o no, pero definitivamente mejora el tiempo de First Contentful Paint. Incluso podemos automatizarlo usando SQIP que crea una versión de baja calidad de una imagen como un marcador de posición SVG, o marcadores de posición de imagen degradada con degradados lineales CSS.

    Estos marcadores de posición se pueden incrustar en HTML, ya que naturalmente se comprimen bien con los métodos de compresión de texto. En su artículo, Dean Hume ha descrito cómo se puede implementar esta técnica usando Intersection Observer.

    ¿Retroceder? Si el navegador no es compatible con el observador de intersecciones, aún podemos cargar un polyfill de forma diferida o cargar las imágenes de inmediato. E incluso hay una biblioteca para ello.

    ¿Quieres ir más elegante? Puede rastrear sus imágenes y usar formas y bordes primitivos para crear un marcador de posición SVG liviano, cargarlo primero y luego pasar de la imagen vectorial del marcador de posición a la imagen de mapa de bits (cargada).

  2. Tres versiones diferentes que muestran la técnica de carga diferida SVG de José M. Pérez, una versión similar al arte cubista a la izquierda, una versión borrosa pixelada en el medio y una imagen propia del propio José a la derecha
    Técnica de carga diferida SVG de Jose M. Perez. (Vista previa grande)
  3. ¿Aplaza el renderizado con content-visibility ?
    Para un diseño complejo con muchos bloques de contenido, imágenes y videos, la decodificación de datos y la representación de píxeles puede ser una operación bastante costosa, especialmente en dispositivos de gama baja. Con content-visibility: auto , podemos indicarle al navegador que omita el diseño de los niños mientras el contenedor está fuera de la ventana gráfica.

    Por ejemplo, puede omitir la representación del pie de página y las últimas secciones en la carga inicial:

    footer { content-visibility: auto; contain-intrinsic-size: 1000px; /* 1000px is an estimated height for sections that are not rendered yet. */ }

    Tenga en cuenta que content-visibility: auto; se comporta como desbordamiento: oculto; , pero puede solucionarlo aplicando padding-left y padding-right en lugar del margin-left: auto; , margin-right: auto; y un ancho declarado. El relleno básicamente permite que los elementos se desborden del cuadro de contenido y entren en el cuadro de relleno sin dejar el modelo de cuadro como un todo y cortarlo.

    Además, tenga en cuenta que podría introducir algunos CLS cuando finalmente se renderice el contenido nuevo, por lo que es una buena idea utilizar contain-intrinsic-size con un marcador de posición del tamaño adecuado ( ¡gracias, Una! ).

    Thijs Terluin tiene muchos más detalles sobre ambas propiedades y cómo el navegador calcula el contain-intrinsic-size , Malte Ubl muestra cómo puede calcularlo y un breve video explicativo de Jake y Surma explica cómo funciona todo.

    Y si necesita obtener un poco más de granularidad, con CSS Containment, puede omitir manualmente el trabajo de diseño, estilo y pintura para los descendientes de un nodo DOM si solo necesita tamaño, alineación o estilos calculados en otros elementos, o si el elemento está actualmente fuera del lienzo.

El rendimiento de representación en la carga inicial es de 2288 ms para la línea de base (izquierda) y 13,464 ms para fragmentos con content-visibility:auto (derecha)
En la demostración, la aplicación content-visibility: auto a áreas de contenido fragmentado brinda un aumento de rendimiento de representación de 7 veces en la carga inicial. (Vista previa grande)
  1. ¿Aplazas la decodificación con decoding="async" ?
    A veces, el contenido aparece fuera de la pantalla, pero queremos asegurarnos de que esté disponible cuando los clientes lo necesiten; idealmente, sin bloquear nada en la ruta crítica, pero decodificando y renderizando de forma asíncrona. Podemos usar decoding="async" para otorgar al navegador permiso para decodificar la imagen del hilo principal, evitando el impacto del usuario en el tiempo de CPU utilizado para decodificar la imagen (a través de Malte Ubl):

    <img decoding="async" … />

    De forma alternativa, para las imágenes fuera de pantalla, podemos mostrar primero un marcador de posición y, cuando la imagen esté dentro de la ventana gráfica, mediante IntersectionObserver, active una llamada de red para que la imagen se descargue en segundo plano. Además, podemos diferir el procesamiento hasta la decodificación con img.decode() o descargar la imagen si la API de decodificación de imágenes no está disponible.

    Al renderizar la imagen, podemos usar animaciones de aparición gradual, por ejemplo. Katie Hempenius y Addy Osmani comparten más información en su charla Speed ​​at Scale: Web Performance Tips and Tricks from the Trenches.

  2. ¿Genera y sirve CSS crítico?
    Para asegurarse de que los navegadores comiencen a mostrar su página lo más rápido posible, se ha convertido en una práctica común recopilar todo el CSS necesario para comenzar a mostrar la primera parte visible de la página (conocida como "CSS crítico" o "CSS en la mitad superior de la página"). ") e incluirlo en línea en el <head> de la página, reduciendo así los viajes de ida y vuelta. Debido al tamaño limitado de los paquetes intercambiados durante la fase de inicio lento, su presupuesto para CSS crítico es de alrededor de 14 KB.

    Si va más allá de eso, el navegador necesitará viajes de ida y vuelta adicionales para obtener más estilos. CriticalCSS y Critical le permiten generar CSS crítico para cada plantilla que esté utilizando. Sin embargo, en nuestra experiencia, ningún sistema automático fue mejor que la recopilación manual de CSS crítico para cada plantilla y, de hecho, ese es el enfoque al que hemos regresado recientemente.

    A continuación, puede incluir CSS crítico en línea y cargar el resto de forma diferida con el complemento Webpack de critters. Si es posible, considere usar el enfoque de inserción condicional utilizado por Filament Group, o convierta el código en línea en activos estáticos sobre la marcha.

    Si actualmente carga su CSS completo de forma asíncrona con bibliotecas como loadCSS, no es realmente necesario. Con media="print" , puede engañar al navegador para que obtenga el CSS de forma asíncrona pero se aplique al entorno de la pantalla una vez que se carga. ( ¡gracias Scott! )

    <!-- Via Scott Jehl. https://www.filamentgroup.com/lab/load-css-simpler/ --> <!-- Load CSS asynchronously, with low priority --> <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'" />

    Al recopilar todo el CSS crítico para cada plantilla, es común explorar solo el área "en la mitad superior de la página". Sin embargo, para diseños complejos, podría ser una buena idea incluir la base del diseño también para evitar costos masivos de recálculo y repintado, lo que perjudicaría su puntuación de Core Web Vitals como resultado.

    ¿Qué sucede si un usuario obtiene una URL que se vincula directamente al medio de la página pero el CSS aún no se ha descargado? En ese caso, se ha vuelto común ocultar contenido no crítico, por ejemplo, con opacity: 0; en CSS integrado y opacity: 1 en el archivo CSS completo y mostrarlo cuando CSS esté disponible. Sin embargo, tiene una desventaja importante , ya que es posible que los usuarios con conexiones lentas nunca puedan leer el contenido de la página. Por eso es mejor mantener siempre visible el contenido, aunque no tenga el estilo adecuado.

    Poner CSS crítico (y otros activos importantes) en un archivo separado en el dominio raíz tiene beneficios, a veces incluso más que en línea, debido al almacenamiento en caché. Chrome abre especulativamente una segunda conexión HTTP al dominio raíz cuando solicita la página, lo que elimina la necesidad de una conexión TCP para obtener este CSS. Eso significa que puede crear un conjunto de archivos CSS críticos (p. ej., Critical- Homepage.css , Critical-Product-Page.css, etc.) y servirlos desde su raíz, sin tener que alinearlos. ( ¡gracias, Felipe! )

    Una palabra de precaución: con HTTP/2, el CSS crítico podría almacenarse en un archivo CSS separado y entregarse a través de un servidor push sin inflar el HTML. El problema es que la inserción del servidor era problemática con muchas trampas y condiciones de carrera en todos los navegadores. Nunca fue compatible de manera constante y tuvo algunos problemas de almacenamiento en caché (consulte la diapositiva 114 en adelante de la presentación de Hooman Beheshti).

    De hecho, el efecto podría ser negativo e inflar los búferes de la red, evitando que se entreguen marcos genuinos en el documento. Por lo tanto, no fue muy sorprendente que, por el momento, Chrome esté planeando eliminar la compatibilidad con Server Push.

  3. Experimente reagrupando sus reglas CSS.
    Nos hemos acostumbrado al CSS crítico, pero hay algunas optimizaciones que podrían ir más allá. Harry Roberts realizó una investigación notable con resultados bastante sorprendentes. Por ejemplo, podría ser una buena idea dividir el archivo CSS principal en sus consultas de medios individuales. De esa forma, el navegador recuperará CSS crítico con alta prioridad y todo lo demás con baja prioridad, completamente fuera de la ruta crítica.

    Además, evite colocar <link rel="stylesheet" /> antes de los fragmentos async . Si las secuencias de comandos no dependen de las hojas de estilo, considere colocar las secuencias de comandos de bloqueo sobre los estilos de bloqueo. Si lo hacen, divide ese JavaScript en dos y cárgalo a cada lado de tu CSS.

    Scott Jehl resolvió otro problema interesante al almacenar en caché un archivo CSS en línea con un trabajador de servicio, un problema común familiar si está utilizando CSS crítico. Básicamente, agregamos un atributo de ID al elemento de style para que sea fácil encontrarlo usando JavaScript, luego una pequeña parte de JavaScript encuentra ese CSS y usa la API de caché para almacenarlo en un caché de navegador local (con un tipo de contenido de text/css ) para su uso en páginas posteriores. Para evitar la inserción en páginas posteriores y, en su lugar, hacer referencia externa a los activos almacenados en caché, configuramos una cookie en la primera visita a un sitio. ¡Voila!

    Vale la pena señalar que el estilo dinámico también puede ser costoso, pero generalmente solo en los casos en que confía en cientos de componentes compuestos renderizados simultáneamente. Entonces, si está usando CSS-in-JS, asegúrese de que su biblioteca CSS-in-JS optimice la ejecución cuando su CSS no tenga dependencias en el tema o los accesorios, y no sobre-componga componentes con estilo . Aggelos Arvanitakis comparte más información sobre los costos de rendimiento de CSS-in-JS.

  4. ¿Transmite respuestas?
    A menudo olvidados y descuidados, los flujos proporcionan una interfaz para leer o escribir fragmentos de datos asincrónicos, de los cuales solo un subconjunto podría estar disponible en la memoria en un momento dado. Básicamente, permiten que la página que realizó la solicitud original comience a trabajar con la respuesta tan pronto como esté disponible la primera parte de los datos, y utilizan analizadores optimizados para transmisión para mostrar progresivamente el contenido.

    Podríamos crear una transmisión a partir de múltiples fuentes. Por ejemplo, en lugar de servir un shell de interfaz de usuario vacío y permitir que JavaScript lo complete, puede permitir que el trabajador del servicio construya una secuencia donde el shell proviene de un caché, pero el cuerpo proviene de la red. Como señaló Jeff Posnick, si su aplicación web funciona con un CMS que el servidor procesa HTML uniendo plantillas parciales, ese modelo se traduce directamente en el uso de respuestas de transmisión, con la lógica de plantillas replicada en el trabajador del servicio en lugar de su servidor. El artículo The Year of Web Streams de Jake Archibald destaca cómo se puede construir exactamente. El aumento de rendimiento es bastante notable.

    Una ventaja importante de la transmisión de la respuesta HTML completa es que el HTML representado durante la solicitud de navegación inicial puede aprovechar al máximo el analizador HTML de transmisión del navegador. Los fragmentos de HTML que se insertan en un documento después de que se haya cargado la página (como es común con el contenido que se completa a través de JavaScript) no pueden aprovechar esta optimización.

    ¿Soporte de navegador? Todavía estoy llegando con soporte parcial en Chrome, Firefox, Safari y Edge que admiten la API y Service Workers que se admiten en todos los navegadores modernos. Y si vuelve a sentirse aventurero, puede consultar una implementación experimental de solicitudes de transmisión, que le permite comenzar a enviar la solicitud mientras sigue generando el cuerpo. Disponible en cromo 85.

Una imagen que resume el uso de datos guardados en Android Chrome y el promedio de visitas o sesiones de imágenes descubiertas por la investigación de Cloudinary en noviembre de 2019 y abril de 2020
El 18% de los usuarios globales de Android Chrome tienen habilitado el modo Lite (también conocido como Save-Data), según la investigación de Cloudinary. (Vista previa grande)
  1. Considere hacer que sus componentes reconozcan la conexión.
    Los datos pueden ser costosos y con una carga útil cada vez mayor, debemos respetar a los usuarios que optan por ahorrar datos mientras acceden a nuestros sitios o aplicaciones. El encabezado de solicitud de sugerencia del cliente Save-Data nos permite personalizar la aplicación y la carga útil para usuarios con restricciones de costo y rendimiento.

    De hecho, puede reescribir las solicitudes de imágenes de alto DPI a imágenes de bajo DPI, eliminar fuentes web, efectos de paralaje sofisticados, previsualizar miniaturas y desplazamiento infinito, desactivar la reproducción automática de video, empujar el servidor, reducir la cantidad de elementos mostrados y degradar la calidad de la imagen, o incluso cambiar la forma en que entrega el marcado. Tim Vereecke ha publicado un artículo muy detallado sobre estrategias de ahorro de datos que presenta muchas opciones para guardar datos.

    ¿Quién está usando save-data ?, te estarás preguntando. El 18% de los usuarios globales de Android Chrome tienen habilitado el Modo Lite (con Save-Data activado), y es probable que el número sea mayor. Según la investigación de Simon Hearne, la tasa de aceptación es más alta en los dispositivos más baratos, pero hay muchos valores atípicos. Por ejemplo: los usuarios de Canadá tienen una tasa de aceptación de más del 34 % (en comparación con el ~7 % en los EE. UU.) y los usuarios del último buque insignia de Samsung tienen una tasa de aceptación de casi el 18 % a nivel mundial.

    Con el modo Save-Data activado, Chrome Mobile brindará una experiencia optimizada, es decir, una experiencia web con proxy con secuencias de comandos diferidas , font-display: swap y carga diferida forzada. Simplemente es más sensato crear la experiencia por su cuenta en lugar de confiar en el navegador para realizar estas optimizaciones.

    Actualmente, el encabezado solo es compatible con Chromium, en la versión Android de Chrome o a través de la extensión Data Saver en un dispositivo de escritorio. Finalmente, también puede usar la API de información de red para entregar costosos módulos de JavaScript, imágenes y videos de alta resolución según el tipo de red. La API de información de red y, específicamente, navigator.connection.effectiveType usan valores de RTT , downlink y de effectiveType (y algunos otros) para proporcionar una representación de la conexión y los datos que los usuarios pueden manejar.

    En este contexto, Max Bock habla de componentes conscientes de la conexión y Addy Osmani habla de servicio de módulos adaptativos. Por ejemplo, con React, podríamos escribir un componente que se represente de manera diferente para diferentes tipos de conexión. Como sugirió Max, un componente <Media /> en un artículo de noticias podría generar:

    • Offline : un marcador de posición con texto alt ,
    • Modo 2G / save-data : una imagen de baja resolución,
    • 3G en pantalla no Retina: una imagen de resolución media,
    • 3G en pantallas Retina: imagen Retina de alta resolución,
    • 4G : un vídeo HD.

    Dean Hume proporciona una implementación práctica de una lógica similar utilizando un trabajador de servicio. Para un video, podríamos mostrar un póster de video de manera predeterminada y luego mostrar el ícono "Reproducir", así como el shell del reproductor de video, los metadatos del video, etc. en mejores conexiones. Como alternativa para los navegadores que no son compatibles, podríamos escuchar el evento canplaythrough y usar Promise.race() para agotar el tiempo de carga de la fuente si el evento canplaythrough no se activa en 2 segundos.

    Si desea profundizar un poco más, aquí hay un par de recursos para comenzar:

    • Addy Osmani muestra cómo implementar el servicio adaptable en React.
    • React Adaptive Loading Hooks & Utilities proporciona fragmentos de código para React,
    • Netanel Basel explora los componentes conscientes de la conexión en Angular,
    • Theodore Vorilas comparte cómo funciona el servicio de componentes adaptables mediante la API de información de red en Vue.
    • Umar Hansa muestra cómo descargar/ejecutar JavaScript costoso de forma selectiva.
  2. Considere hacer que su dispositivo de componentes reconozca la memoria.
    Sin embargo, la conexión de red nos da solo una perspectiva en el contexto del usuario. Yendo más allá, también puede ajustar dinámicamente los recursos en función de la memoria disponible del dispositivo, con la API de memoria del dispositivo. navigator.deviceMemory devuelve la cantidad de RAM que tiene el dispositivo en gigabytes, redondeada a la potencia de dos más cercana. La API también presenta un encabezado de sugerencias de cliente, Device-Memory , que informa el mismo valor.

    Bonificación : Umar Hansa muestra cómo aplazar costosos scripts con importaciones dinámicas para cambiar la experiencia en función de la memoria del dispositivo, la conectividad de red y la concurrencia de hardware.

Un desglose que muestra cómo se priorizan los diferentes recursos en Blink a partir de Chrome 46 y posteriores
Un desglose que muestra cómo se priorizan los diferentes recursos en Blink a partir de Chrome 46 y versiones posteriores. (Crédito de la imagen: Addy Osmani) (Vista previa grande)
  1. Caliente la conexión para acelerar la entrega.
    Use sugerencias de recursos para ahorrar tiempo en dns-prefetch (que realiza una búsqueda de DNS en segundo plano), preconnect (que le pide al navegador que inicie el protocolo de enlace de conexión (DNS, TCP, TLS) en segundo plano), prefetch (que le pide al navegador para solicitar un recurso) y preload (que precarga los recursos sin ejecutarlos, entre otras cosas). Bien soportado en los navegadores modernos, con soporte para Firefox pronto.

    ¿Recuerdas prerender ? La sugerencia de recurso utilizada para solicitar al navegador que construya la página completa en segundo plano para la próxima navegación. Los problemas de implementación fueron bastante problemáticos, desde una enorme huella de memoria y uso de ancho de banda hasta múltiples resultados analíticos registrados e impresiones de anuncios.

    Como era de esperar, quedó obsoleto, pero el equipo de Chrome lo ha recuperado como mecanismo NoState Prefetch. De hecho, Chrome trata la sugerencia previa a la representación como una captación previa de prerender , por lo que todavía podemos usarla hoy. Como explica Katie Hempenius en ese artículo, "al igual que la representación previa, NoState Prefetch obtiene recursos por adelantado ; pero a diferencia de la representación previa, no ejecuta JavaScript ni representa ninguna parte de la página por adelantado".

    NoState Prefetch solo usa ~45 MiB de memoria y los subrecursos que se obtienen se obtendrán con una prioridad de red IDLE . Desde Chrome 69, NoState Prefetch agrega el encabezado Purpose: Prefetch a todas las solicitudes para que se distingan de la navegación normal.

    Además, tenga cuidado con las alternativas y los portales de procesamiento previo, un nuevo esfuerzo hacia el procesamiento previo consciente de la privacidad, que proporcionará la preview insertada del contenido para una navegación fluida.

    El uso de sugerencias de recursos es probablemente la forma más fácil de aumentar el rendimiento y, de hecho, funciona bien. ¿Cuándo usar qué? Como ha explicado Addy Osmani, es razonable precargar recursos que sabemos que es muy probable que se utilicen en la página actual y para navegaciones futuras a través de múltiples límites de navegación, por ejemplo, paquetes de paquetes web necesarios para páginas que el usuario aún no ha visitado.

    El artículo de Addy sobre "Cargar prioridades en Chrome" muestra cómo Chrome interpreta exactamente las sugerencias de recursos, por lo que una vez que haya decidido qué activos son críticos para el procesamiento, puede asignarles una alta prioridad. Para ver cómo se priorizan sus solicitudes, puede habilitar una columna de "prioridad" en la tabla de solicitudes de red de Chrome DevTools (así como en Safari).

    La mayoría de las veces en estos días, usaremos al menos preconnect y dns-prefetch , y seremos cautelosos al usar prefetch , preload y prerender . Tenga en cuenta que incluso con preconnect y dns-prefetch , el navegador tiene un límite en la cantidad de hosts que buscará/conectará en paralelo, por lo que es una apuesta segura ordenarlos según la prioridad ( ¡gracias Philip Tellis! ).

    Dado que las fuentes suelen ser activos importantes en una página, a veces es una buena idea solicitar al navegador que descargue fuentes críticas con preload . Sin embargo, verifique dos veces si realmente ayuda al rendimiento, ya que hay un rompecabezas de prioridades al precargar fuentes: dado que la preload se considera de gran importancia, puede pasar por alto recursos aún más críticos como CSS crítico. ( ¡gracias, Barry! )

    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />

    Dado que <link rel="preload"> acepta un atributo de media , puede optar por descargar recursos de forma selectiva en función de las reglas de consulta de @media , como se muestra arriba.

    Además, podemos usar los atributos imagesrcset e imagesizes para precargar imágenes de héroe descubiertas tardíamente más rápido, o cualquier imagen que se cargue a través de JavaScript, por ejemplo, carteles de películas:

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>

    También podemos precargar el JSON como fetch , para que se descubra antes de que JavaScript lo solicite:

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="fetch" href="foo.com/api/movies.json" crossorigin>

    También podríamos cargar JavaScript dinámicamente, de manera efectiva para la ejecución diferida del script.

    /* Adding a preload hint to the head */ var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link); /* Injecting a script when we want it to execute */ var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);

    Algunas trampas a tener en cuenta: la preload es buena para acercar el tiempo de inicio de descarga de un activo a la solicitud inicial, pero los activos precargados aterrizan en la memoria caché que está vinculada a la página que realiza la solicitud. la preload bien con la caché HTTP: nunca se envía una solicitud de red si el elemento ya está en la caché HTTP.

    Por lo tanto, es útil para recursos descubiertos tardíamente, imágenes destacadas cargadas a través background-image , insertando CSS crítico (o JavaScript) y precargando el resto del CSS (o JavaScript).

    Un ejemplo que usa la portada de la película Greyhound protagonizada por Tom Hanks para mostrar que las imágenes precargadas se cargan antes, ya que no es necesario esperar a que JavaScript las descubra.
    Precargue imágenes importantes antes de tiempo; no es necesario esperar a JavaScript para descubrirlos. (Crédito de la imagen: "Precargar imágenes de héroe descubiertas tardíamente más rápido" por Addy Osmani) (Vista previa grande)

    Una etiqueta de preload puede iniciar una precarga solo después de que el navegador haya recibido el HTML del servidor y el analizador de búsqueda anticipada haya encontrado la etiqueta de preload . La carga previa a través del encabezado HTTP podría ser un poco más rápida, ya que no debemos esperar a que el navegador analice el HTML para iniciar la solicitud (aunque se debate).

    Early Hints ayudará aún más, permitiendo que la precarga se active incluso antes de que se envíen los encabezados de respuesta para el HTML (en la hoja de ruta en Chromium, Firefox). Además, las sugerencias de prioridad nos ayudarán a indicar las prioridades de carga de los scripts.

    Tenga cuidado : si está utilizando preload , as debe definirse o nada se carga, además, las fuentes precargadas sin el atributo crossorigin se recuperarán dos veces. Si está utilizando prefetch , tenga cuidado con los problemas de encabezado de Age en Firefox.

Un gráfico que muestra la primera pintura con contenido (por estado del trabajador del servidor) con un recuento de 0 a 150 en un período de tiempo determinado (en ms)
Con un trabajador de servicio, podemos solicitar solo el mínimo de datos y luego transformar esos datos en un documento HTML completo para mejorar FCP. (a través de Phil Walton) (Vista previa grande)
  1. Utilice trabajadores de servicio para el almacenamiento en caché y los respaldos de la red.
    Ninguna optimización del rendimiento en una red puede ser más rápida que un caché almacenado localmente en la máquina de un usuario (aunque hay excepciones). Si su sitio web se ejecuta a través de HTTPS, podemos almacenar en caché los activos estáticos en un caché de trabajador de servicio y almacenar respaldos sin conexión (o incluso páginas sin conexión) y recuperarlos de la máquina del usuario, en lugar de ir a la red.

    Como sugirió Phil Walton, con los trabajadores de servicios, podemos enviar cargas HTML más pequeñas generando nuestras respuestas mediante programación. Un trabajador de servicio puede solicitar solo el mínimo de datos que necesita del servidor (por ejemplo, un contenido HTML parcial, un archivo Markdown, datos JSON, etc.), y luego puede transformar mediante programación esos datos en un documento HTML completo. Entonces, una vez que un usuario visita un sitio y el trabajador de servicio está instalado, el usuario nunca volverá a solicitar una página HTML completa. El impacto en el rendimiento puede ser bastante impresionante.

    ¿Soporte de navegador? Los trabajadores de servicio cuentan con un amplio apoyo y, de todos modos, el respaldo es la red. ¿ Ayuda a aumentar el rendimiento ? Oh, sí, lo hace. Y está mejorando, por ejemplo, con Background Fetch que también permite cargas/descargas en segundo plano a través de un trabajador de servicio.

    Hay una serie de casos de uso para un trabajador de servicio. Por ejemplo, podría implementar la función "Guardar para fuera de línea", manejar imágenes rotas, introducir mensajes entre pestañas o proporcionar diferentes estrategias de almacenamiento en caché según los tipos de solicitud. En general, una estrategia confiable común es almacenar el shell de la aplicación en la memoria caché del trabajador del servicio junto con algunas páginas críticas, como la página sin conexión, la página principal y cualquier otra cosa que pueda ser importante en su caso.

    Sin embargo, hay algunas trampas a tener en cuenta. Con un trabajador de servicio implementado, debemos tener cuidado con las solicitudes de rango en Safari (si está utilizando Workbox para un trabajador de servicio, tiene un módulo de solicitud de rango). Si alguna vez te topaste con DOMException: Quota exceeded. error en la consola del navegador, luego mira el artículo de Gerardo Cuando 7KB equivalen a 7MB.

    Como escribe Gerardo: "Si está creando una aplicación web progresiva y está experimentando un almacenamiento de caché inflado cuando su trabajador de servicio almacena en caché activos estáticos servidos desde CDN, asegúrese de que exista el encabezado de respuesta CORS adecuado para recursos de origen cruzado, no almacene en caché respuestas opacas con su trabajador de servicio sin querer, opta por activos de imagen de origen cruzado en el modo crossorigin agregando el atributo de origen cruzado a la etiqueta <img> ".

    Hay muchos recursos excelentes para comenzar con los trabajadores de servicios:

    • Mentalidad de trabajador de servicios, que lo ayuda a comprender cómo trabajan los trabajadores de servicios detrás de escena y las cosas que debe comprender al crear uno.
    • Chris Ferdinandi proporciona una gran serie de artículos sobre trabajadores de servicios, que explican cómo crear aplicaciones sin conexión y cubren una variedad de escenarios, desde guardar páginas vistas recientemente sin conexión hasta establecer una fecha de caducidad para elementos en un caché de trabajadores de servicios.

    • Dificultades y prácticas recomendadas de Service Worker, con algunos consejos sobre el alcance, retrasando el registro de un Service Worker y el almacenamiento en caché de Service Worker.
    • Gran serie de Ire Aderinokun sobre "Offline First" con Service Worker, con una estrategia de almacenamiento previo en caché del shell de la aplicación.
    • Service Worker: una introducción con consejos prácticos sobre cómo usar Service Worker para disfrutar de ricas experiencias fuera de línea, sincronizaciones periódicas en segundo plano y notificaciones automáticas.
    • Siempre vale la pena consultar el libro de cocina fuera de línea de Jake Archibald con una serie de recetas sobre cómo hornear su propio trabajador de servicio.
    • Workbox es un conjunto de bibliotecas de trabajadores de servicios creadas específicamente para crear aplicaciones web progresivas.
  2. ¿Está ejecutando trabajadores de servidores en CDN/Edge, por ejemplo, para pruebas A/B?
    En este punto, estamos bastante acostumbrados a ejecutar trabajadores de servicio en el cliente, pero con CDN implementándolos en el servidor, también podríamos usarlos para modificar el rendimiento en el perímetro.

    Por ejemplo, en las pruebas A/B, cuando HTML necesita variar su contenido para diferentes usuarios, podríamos usar Service Workers en los servidores CDN para manejar la lógica. También podríamos transmitir la reescritura de HTML para acelerar los sitios que usan Google Fonts.

Un gráfico que muestra series temporales de instalaciones de trabajadores de servicios en computadoras de escritorio y dispositivos móviles con un porcentaje de páginas a lo largo del tiempo entre enero de 2016 y julio de 2020
Serie temporal de la instalación del trabajador de servicio. Solo el 0,87% de todas las páginas de escritorio registran un trabajador de servicios, según Web Almanac. (Vista previa grande)
  1. Optimizar el rendimiento de renderizado.
    Cada vez que la aplicación es lenta, se nota de inmediato. Por lo tanto, debemos asegurarnos de que no haya demoras al desplazarse por la página o cuando se anima un elemento, y que esté alcanzando constantemente los 60 cuadros por segundo. Si eso no es posible, al menos hacer que los cuadros por segundo sean consistentes es preferible a un rango mixto de 60 a 15. Use el will-change de CSS para informar al navegador qué elementos y propiedades cambiarán.

    Siempre que esté experimentando, depure los repintados innecesarios en DevTools:

    • Mida el rendimiento de representación en tiempo de ejecución. Consulte algunos consejos útiles sobre cómo darle sentido.
    • Para comenzar, consulte el curso gratuito de Udacity de Paul Lewis sobre optimización de la representación del navegador y el artículo de Georgy Marchuk sobre pintura del navegador y consideraciones para el rendimiento web.
    • Habilite Paint Flashing en "Más herramientas → Representación → Paint Flashing" en Firefox DevTools.
    • En React DevTools, marque "Resaltar actualizaciones" y habilite "Registrar por qué se renderizó cada componente",
    • También puede usar Why Did You Render, de modo que cuando se vuelva a renderizar un componente, un flash le notificará el cambio.

    ¿Está utilizando un diseño de mampostería? Tenga en cuenta que es posible que pueda crear un diseño de mampostería solo con la cuadrícula CSS, muy pronto.

    Si desea profundizar en el tema, Nolan Lawson ha compartido trucos para medir con precisión el rendimiento del diseño en su artículo, y Jason Miller también sugirió técnicas alternativas. También tenemos un pequeño artículo de Sergey Chikuyonok sobre cómo obtener una animación GPU correcta.

    Animaciones de alto rendimiento que incluyen Posición, Escala, Rotación y Opacidad
    Los navegadores pueden animar la transformación y la opacidad de forma económica. CSS Triggers es útil para verificar si CSS activa rediseños o reflujos. (Crédito de la imagen: Addy Osmani)(Vista previa grande)

    Nota : los cambios en las capas compuestas por GPU son los menos costosos, por lo que si puede activar solo la composición a través de la opacity y transform , estará en el camino correcto. Anna Migas también brindó muchos consejos prácticos en su charla sobre la depuración del rendimiento de representación de la interfaz de usuario. Y para comprender cómo depurar el rendimiento de pintura en DevTools, consulte el video de auditoría de rendimiento de pintura de Umar.

  2. ¿Ha optimizado para el rendimiento percibido?
    Si bien la secuencia de cómo aparecen los componentes en la página y la estrategia de cómo entregamos los activos al navegador son importantes, no debemos subestimar también el papel del rendimiento percibido. El concepto trata los aspectos psicológicos de la espera, básicamente manteniendo a los clientes ocupados o comprometidos mientras sucede otra cosa. Ahí es donde entran en juego la gestión de la percepción, el inicio preventivo, la finalización anticipada y la gestión de la tolerancia.

    Que significa todo esto? Mientras cargamos activos, podemos tratar de estar siempre un paso por delante del cliente, para que la experiencia se sienta rápida mientras suceden muchas cosas en segundo plano. Para mantener al cliente comprometido, podemos probar pantallas esqueléticas (demostración de implementación) en lugar de cargar indicadores, agregar transiciones/animaciones y básicamente engañar a la UX cuando no hay nada más que optimizar.

    En su estudio de caso sobre The Art of UI Skeletons, Kumar McMillan comparte algunas ideas y técnicas sobre cómo simular listas dinámicas, texto y la pantalla final, así como también cómo considerar el pensamiento de esqueleto con React.

    Sin embargo, tenga cuidado: las pantallas de esqueleto deben probarse antes de implementarse, ya que algunas pruebas mostraron que las pantallas de esqueleto pueden funcionar peor según todas las métricas.

  3. ¿Evita cambios de diseño y repintados?
    En el ámbito del rendimiento percibido, probablemente una de las experiencias más perturbadoras es el cambio de diseño , o reflujos , causados ​​por imágenes y videos reescalados, fuentes web, anuncios inyectados o secuencias de comandos descubiertas tardíamente que llenan los componentes con contenido real. Como resultado, un cliente podría comenzar a leer un artículo solo para ser interrumpido por un salto de diseño sobre el área de lectura. La experiencia suele ser abrupta y bastante desorientadora: y eso es probablemente un caso de prioridades de carga que deben reconsiderarse.

    La comunidad ha desarrollado un par de técnicas y soluciones para evitar reflujos. En general, es una buena idea evitar insertar contenido nuevo sobre contenido existente , a menos que suceda en respuesta a una interacción del usuario. Establezca siempre los atributos de ancho y alto en las imágenes, para que los navegadores modernos asignen el cuadro y reserven el espacio de forma predeterminada (Firefox, Chrome).

    Tanto para imágenes como para videos, podemos usar un marcador de posición SVG para reservar el cuadro de visualización en el que aparecerán los medios. Eso significa que el área se reservará correctamente cuando también necesite mantener su relación de aspecto. También podemos usar marcadores de posición o imágenes alternativas para anuncios y contenido dinámico, así como preasignar espacios de diseño.

    En lugar de imágenes de carga diferida con secuencias de comandos externas, considere usar la carga diferida nativa o la carga diferida híbrida cuando cargamos una secuencia de comandos externa solo si la carga diferida nativa no es compatible.

    Como se mencionó anteriormente, siempre agrupe los repintados de fuentes web y la transición de todas las fuentes alternativas a todas las fuentes web a la vez; solo asegúrese de que ese cambio no sea demasiado abrupto, ajustando la altura de línea y el espacio entre las fuentes con font-style-matcher .

    Para anular las métricas de fuente para que una fuente alternativa emule una fuente web, podemos usar los descriptores @font-face para anular las métricas de fuente (demostración, habilitado en Chrome 87). (Tenga en cuenta que los ajustes son complicados con pilas de fuentes complicadas).

    Para el CSS tardío, podemos asegurarnos de que el CSS crítico para el diseño esté integrado en el encabezado de cada plantilla. Incluso más allá de eso: para páginas largas, cuando se agrega la barra de desplazamiento vertical, desplaza el contenido principal 16px hacia la izquierda. Para mostrar una barra de desplazamiento antes, podemos agregar overflow-y: scroll en html para aplicar una barra de desplazamiento en la primera pintura. Esto último ayuda porque las barras de desplazamiento pueden causar cambios de diseño no triviales debido a que el contenido de la parte superior del pliegue se redistribuye cuando cambia el ancho. Sin embargo, debería ocurrir principalmente en plataformas con barras de desplazamiento no superpuestas como Windows. Pero: rompe la position: sticky porque esos elementos nunca se desplazarán fuera del contenedor.

    Si trabaja con encabezados que se vuelven fijos o fijos en la parte superior de la página al desplazarse, reserve espacio para el encabezado cuando se pinee, por ejemplo, con un elemento de marcador de posición o margin-top en el contenido. Una excepción deberían ser los banners de consentimiento de cookies que no deberían tener impacto en CLS, pero a veces lo hacen: depende de la implementación. Hay algunas estrategias interesantes y conclusiones en este hilo de Twitter.

    Para un componente de pestaña que puede incluir varias cantidades de textos, puede evitar cambios de diseño con pilas de cuadrícula CSS. Al colocar el contenido de cada pestaña en la misma área de cuadrícula y ocultar una de ellas a la vez, podemos asegurarnos de que el contenedor siempre tome la altura del elemento más grande, por lo que no se producirán cambios de diseño.

    Ah, y por supuesto, el desplazamiento infinito y "Cargar más" también pueden causar cambios de diseño si hay contenido debajo de la lista (por ejemplo, pie de página). Para mejorar CLS, reserve suficiente espacio para el contenido que se cargaría antes de que el usuario se desplace a esa parte de la página, elimine el pie de página o cualquier elemento DOM en la parte inferior de la página que pueda ser empujado hacia abajo por la carga del contenido. Además, precargar datos e imágenes para el contenido de la mitad inferior de la página, de modo que cuando un usuario se desplace hasta ese punto, ya esté allí. También puede usar bibliotecas de virtualización de listas como react-window para optimizar listas largas ( ¡gracias, Addy Osmani! ).

    Para asegurarse de contener el impacto de los reflujos, mida la estabilidad del diseño con la API de inestabilidad del diseño. Con él, puede calcular el puntaje de Cambio de diseño acumulativo ( CLS ) e incluirlo como un requisito en sus pruebas, por lo que cada vez que aparece una regresión, puede rastrearla y corregirla.

    Para calcular la puntuación de cambio de diseño, el navegador observa el tamaño de la ventana gráfica y el movimiento de elementos inestables en la ventana gráfica entre dos marcos renderizados. Idealmente, la puntuación sería cercana a 0 . Hay una gran guía de Milica Mihajlija y Philip Walton sobre qué es CLS y cómo medirlo. Es un buen punto de partida para medir y mantener el rendimiento percibido y evitar interrupciones, especialmente para tareas críticas para el negocio.

    Sugerencia rápida : para descubrir qué causó un cambio de diseño en DevTools, puede explorar los cambios de diseño en "Experiencia" en el Panel de rendimiento.

    Bonificación : si desea reducir los reflujos y los repintados, consulte la guía de Charis Theodoulou para Minimizar DOM Reflow/Layout Thrashing y la lista de Paul Irish de Qué fuerza el diseño/reflujo, así como CSSTriggers.com, una tabla de referencia sobre las propiedades de CSS que activan el diseño, la pintura y composición.

Tabla de contenido

  1. Preparándose: planificación y métricas
  2. Establecer metas realistas
  3. Definición del entorno
  4. Optimizaciones de activos
  5. Optimizaciones de compilación
  6. Optimizaciones de entrega
  7. Redes, HTTP/2, HTTP/3
  8. Pruebas y monitoreo
  9. Triunfos rápidos
  10. Todo en una página
  11. Descargar la lista de verificación (PDF, Apple Pages, MS Word)
  12. Suscríbase a nuestro boletín de correo electrónico para no perderse las próximas guías.