Saltar al contenido
< samuelsantana.dev />
Volver al BlogDiagrama: Zone.js verifica cinco componentes (header, cart, sidebar, footer, modal); con signal(), solo se actualiza cart

El Renacimiento de Angular: Signals y el Nuevo Enfoque HTTP

Samuel Santana
Publicado el 07 de julio de 2026(Editado el 25 de julio de 2026)
AngularPerformance

Si sigues de cerca el ecosistema de Angular, sabes que el framework está viviendo su mayor renacimiento desde la reescritura de AngularJS a Angular 2. Con la llegada de las versiones 16 y 17, y la consolidación en las versiones más recientes (18 a 22), Google cambió las reglas del juego.

El foco ahora es claro: Developer Experience (DX), rendimiento y menos boilerplate. Y en el centro de este cambio están dos piezas fundamentales: los Signals y la modernización del HttpClient.

En este artículo, vamos a diseccionar cómo estas herramientas cambian la forma en que arquitectamos aplicaciones escalables.

¿Qué son los Signals y por qué deberían importarte?

Durante años, Angular confió en Zone.js para detectar cambios. ¿El problema? Zone.js es un "mazo". Intercepta eventos asíncronos (clics, timeouts, peticiones HTTP) y, por si acaso, revisa el árbol de componentes entero para ver si algo cambió, al menos en los componentes con la estrategia Default (hoy Eager); los OnPush se omiten si no fueron marcados. En aplicaciones complejas, como plataformas SaaS multi-tenant con paneles de datos densos, esto genera cuellos de botella de rendimiento.

Los Signals traen reactividad de grano fino (fine-grained reactivity). Un Signal es un wrapper alrededor de un valor que notifica a los consumidores solo cuando ese valor realmente cambia. El framework ahora sabe qué componentes leen ese Signal y marca solo esos para actualizar, sin depender de Zone.js para enterarse de que algo cambió.

Los 3 Pilares de los Signals:
  1. signal(): el estado mutable básico.
  2. computed(): estado derivado puro. Solo se recalcula si los Signals de los que depende cambian, y el valor se guarda en caché (memoizado).
  3. effect(): efectos secundarios (como guardar en localStorage o sincronizar una biblioteca imperativa; para tocar el DOM después del renderizado, la API indicada es afterRenderEffect()) que se ejecutan automáticamente cuando cambian sus dependencias.

Ejemplo práctico:

import { Component, signal, computed, effect } from '@angular/core';
import { CurrencyPipe } from '@angular/common';

@Component({
  selector: 'app-cart',
  imports: [CurrencyPipe],
  standalone: true,
  template: `
    <div>
      <p>Cantidad: {{ quantity() }}</p>
      <p>Precio Total: {{ totalPrice() | currency }}</p>
      <button (click)="increment()">Agregar ítem</button>
    </div>
  `
})
export class CartComponent {
  // Estado básico
  quantity = signal(1);
  unitPrice = 50.00;

  // Estado derivado (solo recalcula cuando quantity cambia)
  totalPrice = computed(() => this.quantity() * this.unitPrice);

  constructor() {
    // Efecto secundario enfocado en observabilidad o debug
    effect(() => {
      console.log(`El valor del carrito cambió a: ${this.totalPrice()}`);
    });
  }

  increment() {
    this.quantity.update(q => q + 1);
  }
}
Casos de Uso y Ventajas
  • Ventaja: previsibilidad y rendimiento. Pero no es el fin del ExpressionChangedAfterItHasBeenCheckedError: la propia documentación advierte que propagar estado con effect() todavía puede dispararlo.
  • Casos de uso: gestión de estado local complejo, formularios dinámicos e interfaces de alta interactividad (como dashboards en tiempo real).

El Nuevo Enfoque HTTP: Adiós Módulos, Hola Fetch API

Junto con la reactividad, también evolucionó la forma en que Angular maneja la inyección de dependencias y las peticiones de red. El antiguo HttpClientModule está quedando atrás en la era de los Standalone Components.

A partir de Angular 15 (y consolidado como estándar en 17+), la recomendación es usar provideHttpClient(). Más que un cambio de sintaxis, esta nueva API trajo withFetch().

El poder de withFetch()

Históricamente, HttpClient usaba la API XMLHttpRequest por debajo. Al habilitar withFetch() (de Angular 16.1 a 21), Angular pasa a usar la Fetch API nativa. En Angular 22, fetch pasó a ser el backend predeterminado y withFetch() quedó obsoleto: basta con provideHttpClient(), y withXhr() existe para quien necesita progreso de subida, que la Fetch API no reporta. En Server-Side Rendering (SSR) o Static Site Generation (SSG), usa fetch: el soporte de XHR en el servidor está obsoleto.

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withFetch } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch()) // Angular 16.1–21; en 22+, basta con provideHttpClient()
  ]
};

Uniendo los Dos Mundos: RxJS vs Signals

La pregunta que todo reclutador técnico o Tech Lead hace hoy es: "¿Los Signals van a matar a RxJS?"

La respuesta de un ingeniero senior es: No. Resuelven problemas distintos.

RxJS es excepcional para manejar eventos asíncronos a lo largo del tiempo (como el debouncing de inputs o WebSockets). Los Signals, en cambio, son perfectos para estado síncrono. La belleza del Angular moderno está en la interoperabilidad. Con el paquete @angular/core/rxjs-interop, podemos hacer peticiones HTTP (que devuelven Observables) y transformarlas en Signals de forma elegante con toSignal:

import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { toSignal } from '@angular/core/rxjs-interop';

interface User {
  id: number;
  name: string;
}

@Component({
  selector: 'app-user-profile',
  standalone: true,
  template: `
    @if (user(); as user) {
      <h1>{{ user.name }}</h1>
    } @else {
      <p>Cargando...</p>
    }
  `
})
export class UserProfileComponent {
  private http = inject(HttpClient);

  // Transforma el Observable de la petición HTTP directamente en un Signal
  user = toSignal(this.http.get<User>('/api/user/1'));
}

Nota: la nueva sintaxis de Control Flow (@if, @for) de Angular 17+ combina perfectamente con Signals.

Para lecturas reactivas como esta, Angular 22 estabilizó httpResource() (de @angular/common/http): hace la petición a través del propio HttpClient (interceptores incluidos), expone value(), isLoading() y error() como Signals y cancela la petición pendiente cuando cambia la URL reactiva. Para mutaciones (POST, PUT), la documentación recomienda seguir usando HttpClient directamente.

Conclusión

La ingeniería de software se trata de adoptar los patrones correctos para resolver cuellos de botella de escala. La migración mental hacia los Signals y el uso del nuevo HttpClient con Fetch API no son solo "hype". Son herramientas tangibles que reducen la carga cognitiva del desarrollador y, con el modo zoneless (predeterminado desde Angular 21), sacan Zone.js del bundle y recortan ciclos de detección de cambios innecesarios.

Comentarios

Cargando comentarios...