División de código
Por defecto, un bundler empaqueta toda tu app en un solo archivo JavaScript, y cada import al inicio de un archivo se suma a él. Ese paquete único crece con la app, y el navegador tiene que descargar todo antes de que algo se renderice.
En una máquina rápida con Wi-Fi fuerte puede que nunca lo notes. En un teléfono con mala conexión, una librería pesada puede bloquear toda la carga inicial, incluidas features que no necesitan nada de esa librería. El curso lo demuestra con una lista de productos que depende de un paquete fake-data enorme: con todo en un paquete, incluso el contador en la misma página se queda en blanco durante más de veinte segundos en una conexión limitada.
La división de código es la solución. Divide el paquete en pedazos, llamados chunks, y descarga un chunk solo si y cuando el usuario realmente lo necesita. Las features que el usuario nunca toca jamás se descargan.
import() dinámico
Lo primitivo debajo es JavaScript puro. Un import al inicio del archivo siempre se ejecuta. Llamar a import() como función en cambio carga un módulo bajo demanda, desde donde sea que lo llames en tu código, y retorna una promesa que se resuelve con el módulo.
// siempre cargado, parte del paquete principal
import { formatPrice } from './format'
// cargado solo cuando este handler se ejecuta
async function showChart() {
const { renderChart } = await import('./charts')
renderChart()
}Los bundlers tratan cada llamada a import() como una línea de corte: el módulo importado, más las dependencias que no comparte con el paquete principal, obtienen su propio chunk, descargado por la red la primera vez que se ejecuta la llamada.
lazy y Suspense
React envuelve este primitivo en un par de herramientas. lazy toma una función que retorna un import() dinámico y te devuelve un componente que puedes renderizar como cualquier otro. La promesa del loader tiene que resolverse a un módulo cuya exportación por defecto sea el componente; otras exportaciones nombradas junto a ella están bien. Para un archivo que no controlas que solo exporta componentes nombrados, adapta la promesa en cambio: lazy(() => import('./charts').then(m => ({ default: m.RevenueChart }))).
Renderizar un componente lazy suspende: React pausa el renderizado de esa parte del árbol hasta que llega el chunk. Por sí solo eso es un error, así que envuelves el componente lazy en un límite Suspense con una prop fallback, alguna UI que mostrar mientras la descarga está en vuelo.
import { lazy, Suspense, useState } from 'react'
const ProductsList = lazy(() => import('./ProductsList'))
export default function App() {
const [showProducts, setShowProducts] = useState(false)
return (
<>
<Counter />
<button onClick={() => setShowProducts(prev => !prev)}>
Show products
</button>
{showProducts && (
<Suspense fallback={<h2>Loading...</h2>}>
<ProductsList />
</Suspense>
)}
</>
)
}Ahora el paquete principal ya no contiene ProductsList ni su dependencia pesada. El contador aparece inmediatamente. Al hacer clic en el botón inicia la descarga del chunk, el fallback se muestra mientras carga, y el componente se renderiza cuando llega. Después de esa primera carga el módulo se cachea, así que ocultarlo y mostrarlo de nuevo es instantáneo.
Nota de versión
lazy y Suspense son importaciones nombradas de react. Código más antiguo, incluidas las lecciones del curso, las escribe como React.lazy y React.Suspense; son las mismas APIs.
Divide en rutas primero
Podrías buscar componentes individuales pesados, pero la línea de corte por defecto en la mayoría de apps es la ruta. Cada página es una unidad natural: un visitante en la página de inicio no tiene uso para el código del dashboard, así que haz de cada página un componente lazy y deja que el routing decida qué chunks se descargan.
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
const Home = lazy(() => import('./pages/Home'))
const Vans = lazy(() => import('./pages/Vans'))
const Dashboard = lazy(() => import('./pages/Dashboard'))
export default function App() {
return (
<BrowserRouter>
<Suspense fallback={<h2>Loading...</h2>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/vans" element={<Vans />} />
<Route path="/host" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
)
}Un límite Suspense alrededor de las rutas cubre cada página. La carga inicial descarga el paquete principal más solo el chunk de la URL actual; otras páginas se descargan cuando el usuario navega a ellas. Esa amplitud tiene un costo: navegar a una página cuyo chunk aún no ha llegado reemplaza toda el área de ruta por el fallback, así que mantén el layout compartido como el encabezado y la navegación fuera del límite y se quedan en su lugar durante el cambio.
La división tiene un piso, sin embargo. Cada chunk cuesta una petición extra de red y un momento de UI fallback, así que dividir un componente minúsculo no ahorra nada que valga ese precio. Deja componentes pequeños y cualquier cosa arriba del fold que los usuarios ven inmediatamente en el paquete principal. Divide lo que es pesado, raramente usado, o detrás de una navegación.
Envuelves un import dinámico en lazy, pones el componente dentro de Suspense con un fallback como un mensaje de carga, y React muestra el fallback mientras el código descarga. El resto de tu app aparece de inmediato.
Siguiente: Autenticación, donde sesiones, tokens y ejecución del lado del servidor se unen.

