TechLead
💤
Intermedio
10 min lectura

Lazy Loading y Code Splitting

Carga recursos bajo demanda para mejorar el tiempo inicial

Comprender Lazy Loading

Lazy loading es una estrategia para diferir la carga de recursos no críticos hasta que se necesitan. Mejora el tiempo de carga inicial y reduce el consumo de ancho de banda.

Lazy loading de imágenes

Los navegadores modernos soportan lazy loading nativo en imágenes:

<!-- ❌ Bad: All images load immediately -->
<img src="large-image.jpg" alt="Description">

<!-- ✅ Good: Native lazy loading -->
<img src="large-image.jpg" alt="Description" loading="lazy">

<!-- ✅ Good: With responsive images -->
<img 
  srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
  sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
  src="medium.jpg"
  alt="Description"
  loading="lazy"
>

API Intersection Observer

Para más control y soporte amplio, usa Intersection Observer:

// Custom lazy loading with Intersection Observer
class LazyLoader {
  constructor(options = {}) {
    this.options = {
      root: null,
      rootMargin: '50px',
      threshold: 0.01,
      ...options
    };
    
    this.observer = new IntersectionObserver(
      this.handleIntersection.bind(this),
      this.options
    );
  }
  
  observe(element) {
    this.observer.observe(element);
  }
  
  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        
        // Load image
        if (img.dataset.src) {
          img.src = img.dataset.src;
          img.removeAttribute('data-src');
        }
        
        // Load srcset
        if (img.dataset.srcset) {
          img.srcset = img.dataset.srcset;
          img.removeAttribute('data-srcset');
        }
        
        // Stop observing
        this.observer.unobserve(img);
      }
    });
  }
}

// Usage
const lazyLoader = new LazyLoader({ rootMargin: '100px' });

document.querySelectorAll('img[data-src]').forEach(img => {
  lazyLoader.observe(img);
});

Code Splitting con import()

Divide el bundle y carga módulos bajo demanda:

// Dynamic import - load module only when needed
button.addEventListener('click', async () => {
  const { heavyFunction } = await import('./heavy-module.js');
  heavyFunction();
});

// React lazy loading
import React, { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    Loading...
}> ); }

Lazy loading de rutas

En apps grandes, carga pantallas cuando el usuario las visita:

// React Router lazy loading
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

function Routes() {
  return (
    Loading...
}> } /> } /> ); }

Precarga inteligente

Precarga recursos justo antes de que el usuario los necesite:

// Prefetch on hover
link.addEventListener('mouseenter', () => {
  import('./dashboard-module.js'); // Warm up cache
});

// Prefetch with Intersection Observer
const prefetchObserver = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      import('./below-the-fold.js');
      prefetchObserver.disconnect();
    }
  });
});

prefetchObserver.observe(document.querySelector('#prefetch-trigger'));

Buenas prácticas

  • Usa lazy loading para imágenes y iframes fuera de pantalla
  • Divide bundles grandes con imports dinámicos
  • Prefetch de rutas probables (hover, idle, intersección)
  • Evita demasiados chunks pequeños (overhead de requests)
  • Mide el impacto con Lighthouse y WebPageTest
  • Combina con caché para mejores resultados
Anterior
Debounce y Throttle
Siguiente
Web Workers