Por mais de uma década, a Angular resolveu um problema fundamental –quando verificar a IU novamente depois que algo mudou— com uma solução tão engenhosa quanto invasiva:Zona.js, uma biblioteca que reescreve a API assíncrona do navegador em tempo de execução para interceptar qualquer possível causa de alteração. Com a chegada deSinais, a Angular está gradualmente abandonando essa abordagem em favor de um modelo onde os dados em si, e não um patch global, saibam quem precisa ser atualizado. Este artigo explica como Zone.js funciona internamente, por que seu modelo tem limitações estruturais insolúveis e o que implica na prática criar um aplicativo Angularsem zona.
Como funciona a detecção de alterações baseada em Zone.js
Zone.js resolve um problema muito real: Angular precisa saberQuandoverifique novamente os componentes para atualizar o DOM, mas o JavaScript não oferece nativamente uma maneira de "observar" quando um valor muda de maneira genérica. A solução do Zone.js é drástica: ao inicializar o aplicativo,substitui(monkey-patch) as APIs globais do navegador assíncrono —setTimeout,setInterval,addEventListener,Promessa,Solicitação XMLHttp- para que sempre que uma dessas APIs conclua uma operação, Zone.js a intercepte e notifique o Angular.
O ciclo “generalizado” de detecção de alterações
Quando Zone.js notifica um evento, Angular não sabeo que exatamenteele mudou - ele simplesmente sabe dissoalgo, em algum lugar, pode ter mudado. A resposta é verificar novamente toda a árvore de componentes de cima para baixo (verificação suja), comparando os valores nos templates com aqueles renderizados anteriormente:
// Semplificato: cosa succede concettualmente dopo OGNI evento asincrono
zone.onMicrotaskEmpty.subscribe(() => {
applicationRef.tick(); // ricontrolla l'intero albero dei componenti
});
É por isso que, historicamente, um únicocliqueem um botão em um canto do aplicativo pode fazer com que componentes completamente não relacionados em outras partes da árvore sejam verificados novamente -ChangeDetectionStrategy.OnPushmitiga o problema (ignora subárvores cujas@Entrada()não são alterados por referência), mas não o elimina: Zone.js ainda continua a acionar um loop de controle em cada evento assíncrono, independentemente de levar ou não a uma mudança real.
Os limites estruturais do Zone.js
Zone.js funcionou bem como uma solução "tamanho único", mas sua abordagem tem problemas que não podem ser resolvidos com otimizações incrementais:
1. Sobrecarga de pacote e tempo de execução
Zone.js pesa cerca de 30-35 KB (minificado) no pacote inicial – um custo fixo para cada aplicativo Angular, independentemente de quanto ele realmente usa sua funcionalidade. Em tempo de execução, a aplicação de patches em cada API assíncrona introduz uma sobrecarga mensurável em cada chamada individual, mesmo quando não leva a nenhuma mudança real na IU.
2. Detecção não seletiva de alterações
Zone.js sabeQuealgo aconteceu, mas ele não sabeO que. O resultado é um circuito de controle que, na maioria dos casos, verifica muito mais do que o necessário – mesmo comOnPushativo em todos os lugares, cada evento assíncrono ainda aciona uma rodada de verificação em toda a árvore potencialmente envolvida.
3. Fragilidade com bibliotecas de terceiros
Patching macaco global de APIs comoPromessaoubuscarpode interagir de forma imprevisível com bibliotecas que não esperam que essas APIs sejam reescritas em tempo de execução — é uma causa comum e difícil de diagnosticar de bugs intermitentes em grandes aplicações Angular.
4. Depuração mais complexa
Os rastreamentos de pilha atravessam o código de correção do Zone.js, tornando mais difícil rastrear a origem real de um erro assíncrono – qualquer pessoa que tenha depurado umRejeição de promessa não tratadaem um grande aplicativo Angular conhece esse problema.
O que os sinais mudam
Os sinais invertem o modelo: em vez de “algo aconteceu em algum lugar, verifique tudo de novo”, os próprios dados sabemExatamentequem depende dele, pois a dependência fica explicitamente registrada no momento da leitura:
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
Quandocontagem.set(5)é chamado, Angular sabe exatamente quaiscomputado, qualefeitoe de quais vinculações de modelo elas dependemcontar— não há necessidade de verificar novamente toda a árvore de componentes, porque o gráfico de dependências já é conhecido antecipadamente.
Zone.js vs Signals: comparação direta
| eu espero | Zone.js (detecção de alterações clássica) | Sinais |
|---|---|---|
| Como ele detecta mudanças | Intercepte todas as APIs assíncronas globais | Rastreamento explícito de dependências de leitura |
| O que você verifica | A árvore inteira (ou subárvore com OnPush) | Apenas os consumidores que são efectivamente dependentes |
| Sobrecarga do pacote | ~30-35 KB fixos | Incluído no núcleo, sem custos adicionais |
| Compatibilidade com bibliotecas de terceiros | Risco de conflitos devido à correção de macacos | Não é necessário nenhum patch global |
| Previsibilidade de depuração | Stack traces com patch cruzado | Fluxo de dependência explícito e rastreável |
| Curva de adoção | Funciona "de graça" desde o primeiro dia | Requer refatoração do estado existente |
O que significa "sem zona angular" na prática
Angular zoneless não significa apenas "usar sinais em componentes" - significaremova Zone.js completamentedo pacote e confie toda a detecção de alterações ao gráfico de reatividade dos sinais. É ativado com um provedor dedicado durante a fase de inicialização:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideZonelessChangeDetection } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
provideZonelessChangeDetection(),
// ...altri provider
],
});
Depois que o Zone.js for removido, o Angular não terá mais nenhuma maneira "automática" de perceber uma alteração genérica -Está tudo bemestado que deve ser refletido na UI deve passar explicitamente por um sinal (ou umChangeDetectorRef.markForCheck()manual em casos extremos). É por isso que a migração não é uma simples bandeira de activação, mas uma verdadeira mudança de paradigma na gestão do Estado.
Guia prático: migrar um aplicativo para zoneless
1. Verifique sua cobertura OnPush
Se o aplicativo ainda usaChangeDetectionStrategy.Defaultem muitos componentes, é o primeiro sinal de que o estado não é gerenciado explicitamente - passe tudo paraOnPushé um pré-requisito prático antes mesmo de remover Zone.js.
2. Substitua o estado “implícito” por Sinais
// 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. Verifique bibliotecas de terceiros
Algumas bibliotecas (especialmente componentes de terceiros mais antigos ou códigos que assumem implicitamente a presença de Zone.js para "acordar" o Angular após um evento) podem parar de atualizar a UI corretamente no modo sem zona. Eles devem ser testados individualmente ou embalados em ummarkForCheck()manual nos pontos de integração.
4. Remova Zone.js do pacote
Depois de verificado que todo o estado passa por Signals (ou detecção manual explícita de alterações), Zone.js pode ser removido das dependências e do arquivo polyfill, reduzindo o pacote inicial.
Quando ainda NÃO é conveniente ficar sem zona
- Aplicativos com muitas dependências de terceiros desatualizadas: se bibliotecas críticas assumirem a presença de Zone.js, a migração exigirá testes extensivos caso a caso.
- Bases de código muito grandes com estado gerenciado de forma desigual: se o estado estiver espalhado entre propriedades de classe diretamente mutadas, serviços com RxJS não integrados com Signals e lógica legada, a refatoração necessária é substancial.
- Equipes não familiarizadas com Sinais: zoneless amplifica quaisquer lacunas no gerenciamento explícito de estado — vale a pena solidificar seu conhecimento sobre Signals primeiro com Zone.js ainda ativo como uma rede de segurança.
Perguntas frequentes
Preciso remover Zone.js para usar Signals?
Não, os Signals funcionam perfeitamente mesmo com o Zone.js ainda ativo - é a etapa intermediária mais comum: primeiro adote os Signals para o estado e, em seguida, (opcionalmente) remova o Zone.js quando a cobertura for concluída.
Os sinais substituem o RxJS no Angular?
Não, eles cobrem diferentes casos de uso: os sinais são projetados para estado síncrono local para componentes, o RxJS continua sendo a ferramenta certa para fluxos assíncronos complexos (solicitações HTTP, WebSockets, combinação de vários eventos ao longo do tempo). Os utilitáriosparaSignal()EtoObservável()permitir que os dois modelos coexistam.
O Zoneless já está pronto para produção?
O suporte zoneless é estável nas versões recentes do Angular, mas requer que todo o aplicativo (incluindo bibliotecas de terceiros em uso) seja compatível com um modelo explícito de detecção de alterações — ele deve ser avaliado caso a caso, não é uma simples troca universal.
O que acontece se eu esquecer de usar um Signal para um estado que precisa atualizar a UI?
No modo sem zona, a UI simplesmente não é atualizada até que outro gatilho de detecção de alterações ocorra (por exemplo, um evento de modelo) — é um bug silencioso, e é por isso que a migração requer testes extensivos em cada fluxo.
Os Sinais melhoram o desempenho mesmo sem ficar sem zona?
Sim: mesmo com o Zone.js ainda ativo, as ligações baseadas em sinais nos modelos permitem que o Angular atualize apenas os nós DOM realmente dependentes, reduzindo o trabalho de renderização em comparação com as ligações clássicas.
Preciso reescrever todo o aplicativo para adotar o Signals?
Não, os sinais e o estado "clássico" podem coexistir no mesmo componente e aplicativo — você pode migrar de forma incremental, componente por componente.
Resumindo
Zone.js permitiu que Angular oferecesse detecção automática de alterações “de graça” desde o primeiro dia, mas o preço é um modelo que verifica muito mais do que o necessário, com um custo fixo de pacote e uma fragilidade conhecida na integração com bibliotecas de terceiros. Signals resolve o problema em sua raiz, rastreando dependências explicitamente em vez de interceptar todos os eventos assíncronos do navegador - e abre caminho para um Angular sem zona que é mais leve e previsível para depuração. Se você está considerando quando usar Signals versus RxJS no gerenciamento diário de estado, este guia está vinculado diretamente aSinais angulares vs RxJS: quando usar um e quando o outro.