S

super-dev — full-stack

@super-dev.app · Full-stack · ★ disponible para proyectos
12,8k suscriptores·47 vídeos·Activo desde 2017

Desarrollador full-stack .NET / Angular con perfil DevOps en Azure Cloud. También cómodo con Flutter + Firebase. Construyo productos, los despliego, los monitorizo.

Volver a los artículos
ANGULAR
ANGULAR

Migrar una app Angular a zoneless + signals

Artículo 1 de 4 — Angular 21 en la práctica
Zoneless, signals, resource(), @defer, SignalStore — el frontend moderno, de verdad.

Durante años, Angular se apoyó en zone.js para saber cuándo volver a lanzar la detección de cambios: un monkey patch de todas las API asíncronas del navegador. Funciona, pero es una caja negra costosa. Desde Angular 21, se puede prescindir de él por completo con provideZonelessChangeDetection() y dejar que los signals dirijan la reactividad.

Por qué deshacerse de zone.js

zone.js intercepta setTimeout , las promesas, los eventos del DOM y desencadena un ciclo de detección global cada vez. En una app grande, se verifican miles de bindings cuando solo tres han cambiado. El modo zoneless invierte la lógica: nada se vuelve a pintar mientras un signal leído en el template no haya notificado su cambio.

  • bundle más ligero: se elimina una dependencia de ~100 ko
  • trazas de pila legibles: sin más frames de zone.run por todas partes
  • detección dirigida: solo los componentes que dependen del signal modificado son marcados

Activar el modo zoneless

Todo ocurre en la configuración de la aplicación. Se elimina provideZoneChangeDetection y se conecta el provider zoneless:

TypeScript
1import { provideZonelessChangeDetection } from '@angular/core';
2import { bootstrapApplication } from '@angular/platform-browser';
3
4bootstrapApplication(AppComponent, {
5 providers: [
6 provideZonelessChangeDetection(),
7 provideRouter(routes),
8 ],
9});

Pensar en reactividad fina

Una vez en modo zoneless, todo el estado debe ser un signal , de lo contrario el template deja de actualizarse. Se reemplazan los campos mutables por signal() , los valores derivados por computed() , y los efectos secundarios por effect() :

TypeScript
1@Component({
2 selector: 'app-cart',
3 changeDetection: ChangeDetectionStrategy.OnPush,
4 template: `Total : {{ total() }} €`,
5})
6export class CartComponent {
7 protected readonly items = signal<CartItem[]>([]);
8 protected readonly total = computed(() =>
9 this.items().reduce((sum, item) => sum + item.price * item.quantity, 0),
10 );
11}

Las trampas que hay que conocer

El código legacy que hace setTimeout(() => this.value = x) sin pasar por un signal ya no refrescará la vista. Lo mismo ocurre con las suscripciones RxJS: hay que usar toSignal() o llamar a signal.set() dentro del subscribe . En cuanto a los tests, se cambia Vitest a modo zoneless y se reemplazan los fakeAsync / tick por await fixture.whenStable() . La documentación oficial detalla cada caso en la guide zoneless .

El zoneless no hace que una app sea mágicamente más rápida. Hace que la reactividad sea explícita : sabes exactamente por qué algo se vuelve a pintar, y eso es lo que lo cambia todo al depurar.
super-dev — portfolio.app
// Continuar en ANGULAR
Un motor estilo DOOM en el navegador, con tres backends
11 min • 1,4k lecturas
Cargar datos con resource() y httpResource()
7 min • 3,6k lecturas
@defer y el nuevo control flow: menos JS al arranque
6 min • 2,9k lecturas