Conoces el concepto de imágenes de carga diferida. Evita que el navegador cargue imágenes hasta que esas imágenes estén en (o casi en) la ventana gráfica del navegador.
Hay una gran cantidad de soluciones de carga diferida basadas en JavaScript. GitHub tiene más de 3400 repositorios de carga diferida diferentes, ¡y esos son solo los que tienen “carga diferida” en una cadena de búsqueda! La mayoría se basan en el mismo truco: en lugar de poner la URL de una imagen en el src
atributo, lo pones en data-src
– que es el mismo patrón para imágenes sensibles:
- JavaScript observa al usuario desplazarse hacia abajo en la página
- Cuando el usuario encuentra una imagen, JavaScript mueve la
data-src
valor ensrc
a donde pertenece - El navegador solicita la imagen y se carga a la vista.
El resultado es que el navegador carga menos imágenes por adelantado para que la página se cargue más rápido. Además, si el usuario nunca se desplaza lo suficiente para ver una imagen, esa imagen nunca se carga. Eso equivale a cargas de página más rápidas y menos datos que el usuario necesita gastar.
“¡Esto es increíble!” usted puede estar pensando. Y tienes razón… ¡es increíble!
Dicho esto, de hecho introduce un problema notable: las imágenes que no contienen el src
atributo (incluso cuando está vacío o no es válido) no tienen altura. Esto significa que no tienen el tamaño correcto en el diseño de la página hasta que se cargan de forma diferida.
¡Actualizar! Los tiempos cambiaron rápido aquí, y para evitar la carga diferida, todo lo que tiene que hacer es poner los atributos correctos de ancho y alto natural en las imágenes y se cargarán bien, incluso si CSS hace que la imagen sea fluida. Así que hazlo como: <img src="https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/image.jpg" width="800" height="600">
Cuando un usuario se desplaza y las imágenes se cargan de forma diferida, esas img
los elementos van desde una altura de 0 píxeles hasta lo que necesitan ser. Esto causa reflujo, donde el contenido debajo o alrededor de la imagen se empuja para dejar espacio para la imagen recién cargada. El reflujo es un problema porque es una operación de bloqueo de usuarios. Ralentiza el navegador al obligarlo a recalcular el diseño de los elementos que se ven afectados por la forma de esa imagen. el css scroll-behavior
La propiedad puede ayudar aquí en algún momento, pero su soporte debe mejorar antes de que sea una opción viable.
La carga diferida no garantiza que la imagen se cargue por completo antes de ingresar a la ventana gráfica. El resultado es una experiencia percibida como una tontería, incluso si se trata de una gran ganancia de rendimiento.
Hay otros problemas con las imágenes de carga diferida que vale la pena mencionar pero que están fuera del alcance de esta publicación. Por ejemplo, si JavaScript no se ejecuta en absoluto, no se cargarán imágenes en la página. Esa es una preocupación común para cualquier solución basada en JavaScript, pero este artículo solo trata de resolver los problemas introducidos por el reflujo.
Si pudiéramos forzar las imágenes precargadas para que mantuvieran su anchura y altura normales (es decir, su relación de aspecto), podríamos evitar problemas de reflujo mientras las cargamos de forma diferida. Esto es algo que tuve que resolver recientemente creando una aplicación web progresiva en DockYard donde trabajo.
Para referencia futura, hay un atributo HTML llamado intrinsicsize
eso está diseñado para preservar la relación de aspecto, pero en este momento, eso es solo experimental en Chrome.
Así es como lo hicimos.
Mantener la relación de aspecto
Hay muchas maneras de abordar la forma en que podemos mantener las relaciones de aspecto. Chris una vez redondeó una lista exhaustiva de opciones, pero esto es lo que estamos buscando para opciones específicas de imagen.
la imagen en si
La imagen src
proporciona una relación de aspecto natural. Incluso cuando se cambia el tamaño de una imagen de manera receptiva, se siguen aplicando sus dimensiones naturales. Aquí hay un poco de CSS de imagen receptivo bastante común:
img {
max-width: 100%;
height: auto;
}
Ese CSS le dice a las imágenes que no excedan el ancho del elemento que las contiene, sino que escalen la altura correctamente para que no haya “estiramiento” o “aplastamiento” a medida que se cambia el tamaño de la imagen. Incluso si la imagen tiene en línea height
y width
atributos, este CSS los mantendrá comportándose bien en ventanas pequeñas.
Sin embargo, ese comportamiento de “relación de aspecto natural” se rompe si no hay src
aún. A los navegadores no les importa data-src
y no haga nada con él, por lo que no es realmente una solución viable para el reflujo de carga diferida; pero es importante ayudar a comprender la forma “normal” en que se distribuyen las imágenes una vez que se han cargado.
Un pseudo-elemento
Muchos desarrolladores, incluyéndome a mí, se han sentido frustrados al intentar usar pseudoelementos (por ejemplo, ::before
y ::after
) para añadir decoraciones a img
elementos. Los navegadores no muestran los pseudoelementos de una imagen porque img
es un elemento reemplazado, lo que significa que su diseño está controlado por un recurso externo.
Sin embargo, hay una excepción a esa regla: si una imagen src
El atributo no es válido, los navegadores mostrarán sus pseudoelementos. Entonces, si almacenamos el src
para una imagen en data-src
y el src
está vacío, entonces podemos usar un pseudo-elemento para establecer una relación de aspecto:
[data-src]::before {
content: '';
display: block;
padding-top: 56.25%;
}
Eso establecerá una relación de aspecto de 16:9 en ::before
para cualquier elemento con un data-src
atributo. Tan pronto como data-src
se convierte en el src
, el navegador deja de renderizar ::before
y la relación de aspecto natural de la imagen toma el control.
Aquí hay una demostración:
Vea la relación de aspecto de la imagen del lápiz: :: antes del relleno por James Steinbach (@jdsteinbach) en CodePen.
Sin embargo, hay un par de inconvenientes en esta solución. Primero, se basa en CSS y HTML trabajando juntos. Su hoja de estilo debe tener una declaración para cada relación de aspecto de imagen que necesita admitir. Sería mucho mejor si la plantilla pudiera insertar una imagen sin necesidad de ediciones de CSS.
En segundo lugar, no funciona en Safari 12 y versiones anteriores, o Edge, en el momento de escribir este artículo. Esa es una muestra de tráfico bastante grande para enviar diseños deficientes. Para ser justos, mantener la relación de aspecto es una especie de mejora progresiva: no hay nada “roto” en la página renderizada final. Aún así, es mucho más ideal para resolver el problema del reflujo y para que las imágenes se reproduzcan como se esperaba.
URI de datos (Base64) PNG
Otra forma en la que intentamos conservar la relación de aspecto fue mediante el URI de datos en línea para el src
. como PNG. El uso de png-pixel.com ayudará con el levantamiento de toda esa codificación base64 con cualquier dimensión y color. Esto puede ir directamente a la imagen src
atributo en el HTML:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAQAAAA3fa6RAAAADklEQVR42mNkAANGCAUAACMAA2w/AMgAAAAASUVORK5CYII=" data-src="https://picsum.photos/900/600" alt="Lazy loading test image" />
El PNG en línea tiene una relación de aspecto de 3:2 (la misma relación de aspecto que la imagen final). Cuándo src
se reemplaza con el data-src
valor, la imagen mantendrá su relación de aspecto exactamente como queremos!
Aquí hay una demostración:
Consulte la relación de aspecto de la imagen del lápiz: PNG base64 en línea de James Steinbach (@jdsteinbach) en CodePen.
Y, sí, este enfoque también tiene algunos inconvenientes. Aunque el soporte del navegador es mucho mejor, es complicado de mantener. Necesitamos generar una cadena base64 para cada nuevo tamaño de imagen, luego hacer que ese objeto de cadenas esté disponible para cualquier herramienta de plantilla que se esté utilizando. Tampoco es la forma más eficiente de representar estos datos.
Seguí explorando y encontré una forma más pequeña.
Combinar SVG con base64
Después de explorar la opción PNG en línea, me preguntaba si SVG podría ser un formato más pequeño para imágenes en línea y esto es lo que encontré: un SVG con un viewBox
La declaración es una imagen de marcador de posición con una relación de aspecto nativa fácilmente editable.
Primero, probé la codificación base64 de un SVG. Aquí hay un ejemplo de cómo se veía eso en mi HTML:
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAzIDInPjwvc3ZnPg==" data-src="https://picsum.photos/900/600" alt="Lazy loading test image">
En relaciones de aspecto pequeñas y simples, esto es aproximadamente equivalente en tamaño a los PNG base64. Una proporción de 1:1 sería de 114 bytes con base64 PNG y 106 bytes con base64 SVG. Una proporción de 2:3 es de 118 bytes con base64 PNG y 106 bytes con base64 SVG.
Sin embargo, el uso de SVG base64 para proporciones más grandes y complejas se mantiene pequeño, lo que es un verdadero ganador en cuanto al tamaño del archivo. Una proporción de 16:9 es de 122 bytes en base64 PNG y 110 bytes en base64 SVG. ¡Una proporción de 923:742 es de 3100 bytes en base64 PNG pero solo 114b en base64 SVG! (Esa no es una relación de aspecto común, pero necesitaba probar con dimensiones personalizadas con el caso de uso de mi cliente).
Aquí hay una tabla para ver esas comparaciones más claramente:
Relación de aspecto | base64 PNG | base64 SVG |
---|---|---|
1:1 | 114 bytes | 106 bytes |
2:3 | 118 bytes | 106 bytes |
16:9 | 122 bytes | 110 bytes |
923:742 | 3100 bytes | 114 bytes |
Las diferencias son insignificantes con proporciones simples, pero puede ver cuán bien escala SVG a medida que las proporciones se vuelven complejas.
Ahora tenemos mucho mejor soporte de navegador. Esta técnica es compatible con todos los grandes jugadores, incluidos Chrome, Firefox, Safari, Opera, IE11 y Edge, pero también tiene un gran soporte en los navegadores móviles, incluidos Safari iOS, Chrome para Android y Samsung para Android (desde 4.4 en adelante) .
Aquí hay una demostración:
Consulte la relación de aspecto de la imagen del lápiz: SVG base64 en línea de James Steinbach (@jdsteinbach) en CodePen.
🏆 ¡Tenemos ganador!
Sí, lo hacemos, ¡pero quédate conmigo mientras mejoramos este enfoque aún más! Recordé que Chris sugirió que no deberíamos usar la codificación base64 con SVG integrado en imágenes de fondo CSS y pensé que ese consejo también podría aplicarse aquí.
En este caso, en lugar de codificar en base64 los SVG, utilicé la técnica “Codificación de URL optimizada” de esa publicación. Aquí está el marcado:
<img src="data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox='0 0 3 2'%3E%3C/svg%3E" data-src="https://picsum.photos/900/600" alt="Lazy loading test image" />
Esto es solo un poco más pequeño que base64 SVG. El 1:1 tiene 106 bytes en base64 y 92 bytes cuando se codifica en URL. 16:9 emite 110 bytes en base64 y 97 bytes cuando está codificado en URL.
Si está interesado en más tamaño de datos por archivo y formato de codificación, esta demostración compara diferentes tamaños de bytes entre todas estas técnicas.
Sin embargo, los beneficios reales que hacen que el SVG codificado en URL sea un claro ganador es que su formato es legible por humanos, fácil de crear plantillas e infinitamente personalizable.
¡No necesita crear un bloque CSS o generar una cadena base64 para obtener un marcador de posición perfecto para imágenes donde se desconocen las dimensiones! Por ejemplo, aquí hay un pequeño componente React que usa esta técnica:
const placeholderSrc = (width, height) => `data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"%3E%3C/svg%3E`
const lazyImage = ({url, width, height, alt}) => {
return (
<img
src={placeholderSrc(width, height)}
data-src={url}
alt={alt} />
)
}
Vea la imagen Pen React LazyLoad con relación de aspecto estable de James Steinbach (@jdsteinbach) en CodePen.
O, si prefiere Vue:
Vea la imagen Pen Vue LazyLoad con relación de aspecto estable de James Steinbach (@jdsteinbach) en CodePen.
Me complace informar que la compatibilidad con navegadores no ha cambiado con esta mejora. ¡Aún contamos con la compatibilidad completa como base64 SVG!
Conclusión
Hemos explorado varias técnicas para evitar el reflujo de contenido conservando la relación de aspecto de una imagen con carga diferida antes de que ocurra el intercambio. La mejor técnica que pude encontrar es SVG codificado en URL integrado y optimizado con dimensiones de imagen definidas en el viewBox
atributo. Eso se puede programar con una función como esta:
const placeholderSrc = (width, height) => `data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"%3E%3C/svg%3E`
Los beneficios de esta técnica son varios:
- Sólida compatibilidad con navegadores en computadoras de escritorio y dispositivos móviles
- Tamaño de byte más pequeño
- formato legible por humanos
- Fácil creación de plantillas sin llamadas de codificación en tiempo de ejecución
- infinitamente extensible
Que piensas de esta aproximación? ¿Ha usado algo similar o tiene una forma completamente diferente de manejar el reflujo? ¡Hágamelo saber!