Lección 02

El navegador por dentro

Todo lo que llamamos "lento" pasa por el mismo pasillo, y ese pasillo tiene una sola puerta. Entenderla es lo que convierte una queja en un diagnóstico.

01Qué es

Cuando abres una página, el navegador no guarda una fotografía: guarda una lista de piezas. Cada botón, cada texto, cada imagen es una pieza con su posición y su estilo. Esa lista se llama DOM, y es la única cosa que el navegador sabe dibujar.

Y aquí está el detalle que lo explica casi todo: el navegador trabaja con un solo par de manos. Colocar las piezas, pintarlas, ejecutar tu JavaScript y responder a tus clics pasan por el mismo sitio, que se llama hilo principal. No hay dos pasillos: hay uno.

La analogía útil es una cocina con un solo cocinero. Puede cortar, cocinar o atender al cliente, pero no las tres cosas a la vez. Si está cortando cebolla durante dos minutos, el cliente que levantó la mano espera dos minutos — aunque el plato esté casi listo.

02Por qué sucede

Para convertir tu código en píxeles, el navegador hace siempre los mismos cinco pasos:

  1. DOM: lee el HTML y construye el árbol de piezas.
  2. CSSOM: lee los estilos y construye el árbol de reglas.
  3. Árbol de render: cruza los dos anteriores; decide qué se dibuja y con qué estilo.
  4. Layout: calcula dónde va cada pieza y cuánto mide. Aquí es donde "mover una silla obliga a rodear la mesa entera".
  5. Paint y composición: rellena los píxeles.

Los pasos 3, 4 y 5 se repiten cada vez que algo cambia. Y aquí aparece la primera regla incómoda: cambiar el tamaño o la posición de una pieza puede obligar a recalcular todas las demás. Por eso una lista de 5.000 filas no es "5.000 veces una fila": es un cálculo que las incluye a todas.

La segunda regla es el presupuesto de fotograma. Para que una animación se vea fluida, el navegador tiene unos 16,7 milisegundos por fotograma (60 por segundo). Todo lo que hagas —tu JavaScript, el layout, el paint— tiene que caber ahí. Si una sola tarea ocupa 120 ms, el navegador se salta siete fotogramas seguidos: eso es el tirón.

De HTML a píxeles: cinco pasos, un solo hilo HTML → DOM CSS → CSSOM árbol de render layout paint píxeles aquí se va el tiempo
Los pasos 3, 4 y 5 se repiten en cada cambio. Nodos de más y trabajos de más caen siempre sobre los mismos tres.
16,7 ms por fotograma; una tarea larga se come siete fotogramas ideales 0 ms 133 ms con una tarea de 120 ms JavaScript de un filtro sin índice el clic del usuario espera aquí 120 ms ≈ 7 fotogramas perdidos
Por debajo de 50 ms una tarea no se nota. Por encima, cada milisegundo es un clic que el navegador todavía no puede atender.

03Cómo se ataca

Con la cadena en la cabeza, solo existen cuatro formas de intervenir:

Reducir lo que hay

Quitar piezas de la mesa: no montar lo que no se ve, paginar, no renderizar datos que nadie mira.

Reducir lo que haces

Menos trabajo por fotograma: índices, cálculos fuera del render, debounce cuando de verdad aplica.

Mover el trabajo

Sacar cómputo del hilo principal: un Web Worker. El pasillo queda libre; se paga en copiar datos.

No tocar el layout

Animar con transform y opacity evita recalcular posiciones; animar con top o width las recalcula todas.

El patrón que más caro sale

Leer una medida del DOM y escribir un estilo, en bucle. Por ejemplo: leer offsetHeight de cada fila y luego fijar su altura. Eso obliga al navegador a recalcular el layout en cada vuelta del bucle. Es lo que se llama layout thrashing, y convierte un bucle de 100 elementos en 100 recálculos completos.

// MAL: alterna lectura y escritura → 100 recálculos de layout
for (const fila of filas) {
  const alto = fila.offsetHeight;   // leer fuerza layout
  fila.style.height = alto + 1 + 'px'; // escribir lo invalida
}

// BIEN: primero leo todo, después escribo todo → un solo recálculo
const altos = filas.map((fila) => fila.offsetHeight);
filas.forEach((fila, i) => {
  fila.style.height = altos[i] + 1 + 'px';
});

04Cómo diagnosticarlo

La regla de oro: no mires, graba. El panel de Performance graba lo que pasó y te deja ver el orden real de las cosas, cosa que la intuición siempre equivoca.

HerramientaQué se miraCómo se ve el síntoma
Performance › grabar una interacción Long Tasks Barras rojas de más de 50 ms con un nombre de función dentro
Performance › Main Quién ocupa el hilo Un bloque amarillo enorme de JavaScript donde debía haber muchos pequeños
Performance › Rendering Layout y paint repetidos Barras moradas y verdes encadenadas; "Recalculate Style" muchas veces por segundo
Performance › CPU 4x slowdown El mismo caso en una máquina lenta Un tirón que en tu equipo no se ve aparece con claridad
La trampa de la máquina rápida

Tu laptop es más rápida que el teléfono de quien usa el producto. Si no reproduces el problema con la CPU frenada a propósito, no lo estás viendo: lo estás adivinando. Y en una entrevista eso se nota cuando te preguntan "¿y cómo confirmaste que era eso?".

Para decirlo en la entrevista

“The browser has a single main thread, and layout, paint and my JavaScript all compete for it.”

“Anything above fifty milliseconds is a long task, and long tasks are what users feel as lag.”

“I'd record the interaction on a throttled CPU before touching the code.”

Comprueba que lo tienes

Cuatro preguntas. Al comprobar se marca la lección como completada.

¿Por qué un clic puede tardar en responder aunque la página "no esté haciendo nada"?

Quieres que una barra se deslice suavemente. ¿Cuál de las dos formas evita recalcular posiciones?

Un bucle lee offsetHeight y luego escribe style.height, por cada fila. ¿Qué está pasando?

¿Cuál es el umbral que se usa para decir que una tarea es "larga"?