Fran Linde Blázquez
Ingeniero de Software
Desarrollador Webs/Apps desde 2013
Desarrollador Front-End en Minsait desde 2016
1. Lazy Load Intro
2. Implementación Genérica
3. Lazy Load en imágenes
4. Lazy Load en vídeos
5. Lazy Load en JS
6. Librerías
7. Extra Balls
8. Conclusiones y Q&A
Las páginas web están repletas de ficheros estáticos (JS, CSS, IMGS, VIDEOS, HTML..)
Es posible que estemos cargando estos recursos y que el usuario nunca los vea, lo cual significa:
lazy load = cargar recursos cuando sean necesarios
imgs y vids son los recursos más pesados
En ocasiones HTML o JS también son objetivo de l.l.
Ejemplos de páginas con "problemas":
50 prods -> 50 imgs
50 p. + 5 slides -> 250 imgs
250 x 200KB = 50MB
100.000 visitas
5TB diarios
$0.09/GB -> 450$/día
13K$/mes (esta página)
Tradicionalmente se ha aplicado lazy load.
Ocultamos elementos y esperamos a que sean visibles.
Observamos: scroll, resize y orientation
HTML:
CSS:
.lazy-image {
/* importante -> "reserva hueco" */
min-height: 200px;
background: #DDD;
}
.lazy-image.loaded {
display: block;
background: none;
}
let lazyImages = [...document.querySelectorAll('.lazy-image')];
function lazyLoad() {
lazyImages.forEach(image => {
let topDistance = image.parentElement.offsetTop;
if (topDistance < window.innerHeight + window.pageYOffset + 100) {
image.src = image.dataset.src;
image.onload = () => image.classList.add('loaded');
}
});
};
lazyLoad();
window.addEventListener('scroll', throttle(lazyLoad, 16));
window.addEventListener('resize', throttle(lazyLoad, 16));
Permite "escuchar" cuando un elemento (target) intersecta con otro elemento (root) o con el viewport
Mismo HTML / CSS
let lazyImages = [...document.querySelectorAll(".lazy-image")];
let lazyImageObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImageObserver.unobserve(lazyImage);
lazyImage.onload = () => lazyImage.classList.add('loaded');
}
});
});
lazyImages.forEach((lazyImage) => {
lazyImageObserver.observe(lazyImage);
});
Can I use IntersectionObserver?
Pueden ser cargadas como src...
O inline y evitas la petición HTTP 😏
Desde Chrome v.76 tenemos el atr. 'loading'
La idea es la misma, pero sin programar 😝
"srcset" indica un set de imgs con su ancho natural
"sizes" indica el ancho a usar (lienzo) dependiendo del ancho del navegador (media query)
Fallback: CSS y media queries...
También existe (draft) la función image-set en css:
background-image: image-set(
url(examples/images/image-384.jpg) 1x,
url(examples/images/image-768.jpg) 2x,
);
Si las imágenes pesan imaginad los vídeos...
Depende del escenario en que nos encontremos
Debemos hacer uso de la etiqueta preload
Indica al navegador cuánto cargar antes del play
auto: permite que el navegador decida
metadata: solo info mínima: poster, duración...
none: no hace preload 👍
Buen soporte
Implementación sencilla:
Típicos donde antes había un gif
Aplica implementación genérica (cualquiera)
Importante: poster y muted
Si estás usando un framework... aprovéchalo
Hacen code splitting con Webpack
Puedes hacer tú mismo lazy load de JS...
Webpack + Promises:
if (iWantToLoadMyModule) {
import('myModule').then(myModule => {
// do something with myModule.default
});
}
Classic Callback:
if (browserSupportsAllFeatures()) {
main();
} else {
// Load polyfill then run main
loadScript('/path/to/polyfills.js', main);
}
Elección: lazysizes es SEO friendly, no requiere inicialización, varios plugins, setea el "sizes" atutom. Contra: requiere polyfill.
Request Order: DNS Lookup, Initial Connection, SSL Negotiation, Time to First Byte (TTFB), Download
Podemos adelantar esto añadiendo la etiqueta:
Reduce el peso de las imágenes (tiny png/jpg)
Revisa server: e-tag, http2, gzip/brotli
Elimina la pista de audio de los videos "muted"
script defer / async
Google chrome’s Lighthouse
Muy interesante: SQIP (NODE)
Piensa 2 veces al importar un estático
Hay muchas librerías que pueden agilizar
No existe LA SOLUCIÓN: Pensemos en perezoso