Durante más de una década, Angular ha resuelto un problema fundamental:cuándo volver a comprobar la interfaz de usuario después de que algo haya cambiado— con una solución tan ingeniosa como invasiva:Zona.js, una biblioteca que reescribe la API asincrónica del navegador en tiempo de ejecución para interceptar cualquier posible causa de cambio. Con la llegada deSeñalesAngular está abandonando gradualmente este enfoque en favor de un modelo en el que los datos en sí, y no un parche global, saben quién necesita ser actualizado. Este artículo explica cómo funciona internamente Zone.js, por qué su modelo tiene limitaciones estructurales irresolubles y qué implica en la práctica crear una aplicación Angular.sin zona.
Cómo funciona la detección de cambios basada en Zone.js
Zone.js resuelve un problema muy real: Angular necesita saberCuandoVuelva a verificar los componentes para actualizar el DOM, pero JavaScript no ofrece de forma nativa una forma de "observar" cuando un valor cambia de forma genérica. La solución de Zone.js es drástica: al iniciar la aplicación,sobrescribe(monkey-patch) las API del navegador asincrónico global:establecer tiempo de espera,establecer intervalo,agregarEventListener,Promesa,Solicitud XMLHttp– para que cada vez que una de estas API complete una operación, Zone.js la intercepte y notifique a Angular.
El ciclo de detección de cambios "generalizado"
Cuando Zone.js notifica un evento, Angular no lo sabeque exactamenteél ha cambiado, él simplemente lo sabealgo, en alguna parte, puede que haya cambiado. La respuesta es volver a verificar todo el árbol de componentes de arriba a abajo (verificación sucia), comparando los valores en las plantillas con los renderizados anteriormente:
// Semplificato: cosa succede concettualmente dopo OGNI evento asincrono
zone.onMicrotaskEmpty.subscribe(() => {
applicationRef.tick(); // ricontrolla l'intero albero dei componenti
});
Por eso, históricamente, un solohacer clicpresionar un botón en una esquina de la aplicación puede hacer que se vuelvan a verificar componentes completamente no relacionados en otras partes del árbol:ChangeDetectionStrategy.OnPushmitiga el problema (omite los subárboles cuyo@Aporte()no se modifican por referencia), pero no lo elimina: Zone.js aún continúa activando un bucle de control en cada evento asincrónico, ya sea que conduzca o no a un cambio real.
Los límites estructurales de Zone.js
Zone.js funcionó bien como una solución "única para todos", pero su enfoque tiene problemas que no se pueden resolver con optimizaciones incrementales:
1. Paquete y sobrecarga de tiempo de ejecución
Zone.js pesa alrededor de 30-35 KB (minificado) en el paquete inicial: un costo fijo para cada aplicación Angular, independientemente de cuánto use realmente su funcionalidad. En tiempo de ejecución, aplicar parches a cada API asincrónica introduce una sobrecarga mensurable en cada llamada individual, incluso cuando no genera ningún cambio real en la interfaz de usuario.
2. Detección de cambios no selectiva
Zone.js lo sabeEsoalgo paso pero el no lo sabeQué. El resultado es un circuito de control que en la mayoría de los casos verifica mucho más de lo necesario, incluso conAl empujaractivo en todas partes, cada evento asincrónico aún activa una ronda de verificación en todo el árbol potencialmente involucrado.
3. Fragilidad con bibliotecas de terceros
Parche global de API comoPromesaobuscarpuede interactuar de manera impredecible con bibliotecas que no esperan que estas API se reescriban en tiempo de ejecución; es una causa común y difícil de diagnosticar de errores intermitentes en aplicaciones Angular grandes.
4. Depuración más compleja
Los seguimientos de pila atraviesan el código de parcheo de Zone.js, lo que hace que sea más difícil rastrear la fuente real de un error asincrónico: cualquiera que haya depurado uno.No controladoPromesaRechazoen una aplicación Angular grande conoce este problema.
Lo que cambian las señales
Signals invierte el modelo: en lugar de "algo pasó en alguna parte, revisa todo nuevamente", los datos mismos lo sabenExactamentequién depende de él, porque la dependencia queda expresamente registrada en el momento de la lectura:
import { signal, computed, effect } from '@angular/core';
const count = signal(0);
const doubled = computed(() => count() * 2); // dipendenza tracciata automaticamente
effect(() => {
console.log('Il valore doppio è', doubled());
// questo effect si ri-esegue SOLO quando doubled() cambia realmente,
// non ad ogni evento asincrono dell'applicazione
});
count.set(5); // notifica solo i consumer reali: doubled, e l'effect
Cuandocontar.conjunto(5)se llama, Angular sabe exactamente cuálescalculado, cualefectoy de qué enlaces de plantilla dependencontar— no es necesario volver a comprobar todo el árbol de componentes, porque el gráfico de dependencia ya se conoce de antemano.
Zone.js vs Signals: comparación directa
| espero | Zone.js (detección de cambios clásica) | Señales |
|---|---|---|
| Cómo detecta cambios | Intercepta todas las API asincrónicas globales | Seguimiento explícito de dependencias de lectura. |
| ¿Qué revisas dos veces? | El árbol completo (o subárbol con OnPush) | Sólo los consumidores que son realmente dependientes |
| Paquete de gastos generales | ~30-35 KB fijos | Incluido en el núcleo, sin costes adicionales |
| Compatibilidad con bibliotecas de terceros | Riesgo de conflictos por el parcheo de monos | No se necesitan parches globales |
| Previsibilidad de depuración | Parches cruzados de seguimientos de pila | Flujo de dependencia explícito y rastreable |
| Curva de adopción | Funciona "gratis" desde el primer día | Requiere refactorización del estado existente. |
¿Qué significa en la práctica "sin zona angular"?
Angular sin zona no solo significa "usar señales en componentes", significaeliminar Zone.js por completodel paquete y confíe toda la detección de cambios al gráfico de reactividad de señales. Se activa con un proveedor dedicado durante la fase de arranque:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideZonelessChangeDetection } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
provideZonelessChangeDetection(),
// ...altri provider
],
});
Una vez que se elimina Zone.js, Angular ya no tiene ninguna forma "automática" de notar un cambio genérico:todo esta bienEl estado que debe reflejarse en la UI debe pasar explícitamente a través de una señal (o unaChangeDetectorRef.markForCheck()manual en casos extremos). Por eso la migración no es una simple bandera a activar, sino un verdadero cambio de paradigma en la gestión estatal.
Guía práctica: migrar una aplicación a zona sin zona
1. Consulta tu cobertura OnPush
Si la aplicación todavía usaChangeDetectionStrategy.Defaulten muchos componentes, es la primera señal de que el estado no se gestiona explícitamente: pasar todo aAl empujares un requisito previo práctico incluso antes de eliminar Zone.js.
2. Reemplace el estado "implícito" con Señales
// Prima: proprietà di classe normale, aggiornata via mutazione diretta
export class CartComponent {
itemCount = 0;
addItem(): void {
this.itemCount++; // senza Zone.js, la UI non si aggiornerebbe
}
}
// Dopo: signal, aggiornamento esplicito e tracciato
export class CartComponent {
itemCount = signal(0);
addItem(): void {
this.itemCount.update(n => n + 1); // il template si aggiorna automaticamente
}
}
3. Consulte bibliotecas de terceros
Algunas bibliotecas (especialmente componentes de terceros más antiguos o código que asume implícitamente la presencia de Zone.js para "activar" Angular después de un evento) pueden dejar de actualizar la interfaz de usuario correctamente en el modo sin zonas. Deben probarse individualmente o envolverse en unomarcarParaVerificar()manual en los puntos de integración.
4. Elimina Zone.js del paquete
Una vez que se ha verificado que todo el estado pasa por Signals (o por detección de cambios manual explícito), Zone.js se puede eliminar de las dependencias y del archivo polyfill, reduciendo el paquete inicial.
Cuando todavía NO es conveniente pasar a zona sin zona
- Aplicaciones con muchas dependencias de terceros obsoletas: Si las bibliotecas críticas asumen la presencia de Zone.js, la migración requiere pruebas exhaustivas caso por caso.
- Bases de código muy grandes con estado administrado de manera desigual: si el estado está disperso entre propiedades de clase directamente mutadas, servicios con RxJS no integrados con Signals y lógica heredada, la refactorización necesaria es sustancial.
- Equipos que no están familiarizados con las señales: sin zona amplifica cualquier brecha en la gestión del estado explícito; primero vale la pena solidificar su conocimiento de Señales con Zone.js aún activo como red de seguridad.
Preguntas frecuentes
¿Necesito eliminar Zone.js para usar Signals?
No, Signals funciona perfectamente incluso con Zone.js aún activo; es el paso intermedio más común: primero adopte Signals para el estado y luego (opcionalmente) elimine Zone.js cuando se complete la cobertura.
¿Las señales reemplazan a RxJS en Angular?
No, cubren diferentes casos de uso: las señales están diseñadas para el estado síncrono local de los componentes, RxJS sigue siendo la herramienta adecuada para flujos asíncronos complejos (solicitudes HTTP, WebSockets, combinación de múltiples eventos a lo largo del tiempo). Las utilidadesa señal()YaObservable()permitir que los dos modelos coexistan.
¿Zonless ya está listo para la producción?
El soporte sin zonas es estable en versiones recientes de Angular, pero requiere que toda la aplicación (incluidas las bibliotecas de terceros en uso) sea compatible con un modelo de detección de cambios explícito; debe evaluarse caso por caso, no es un simple cambio universal.
¿Qué sucede si olvido usar Signal para un estado que necesita actualizar la interfaz de usuario?
En el modo sin zonas, la interfaz de usuario simplemente no se actualiza hasta que ocurre otro activador de detección de cambios (por ejemplo, un evento de plantilla); es un error silencioso, razón por la cual la migración requiere pruebas exhaustivas en cada flujo.
¿Las señales mejoran el rendimiento incluso sin quedar sin zona?
Sí: incluso con Zone.js todavía activo, los enlaces basados en señales en las plantillas permiten a Angular actualizar solo los nodos DOM realmente dependientes, lo que reduce el trabajo de renderizado en comparación con los enlaces clásicos.
¿Necesito reescribir toda la aplicación para adoptar Signals?
No, las señales y el estado "clásico" pueden coexistir en el mismo componente y aplicación; puede migrar de forma incremental, componente por componente.
En resumen
Zone.js ha permitido a Angular ofrecer detección automática de cambios "gratis" desde el primer día, pero el precio es un modelo que verifica mucho más de lo que necesita, con un costo de paquete fijo y una fragilidad conocida en la integración con bibliotecas de terceros. Signals resuelve el problema desde la raíz, rastreando las dependencias explícitamente en lugar de interceptar cada evento asincrónico del navegador, y allana el camino para un Angular sin zonas que es más liviano y más predecible de depurar. Si está considerando cuándo usar Signals versus RxJS en la gestión del estado del día a día, esta guía enlaza directamente conAngular Signals vs RxJS: cuándo usar uno y cuándo el otro.