O fim do Virtual DOM: por que os Signals do SolidJS garantem performance O(1) sem re-renderizações
Descubra como a reatividade fina do SolidJS e os Signals estão mudando o front-end, eliminando o Virtual DOM e o custo das re-renderizações.

Há uma verdade estranha sobre a última década do front-end: dedicamos nossa inteligência técnica mais refinada a tornar uma gambiarra mais rápida.
A gambiarra era o Virtual DOM. Quando seu dado muda, o React reconstrói uma cópia da sua interface na memória, compara com a cópia anterior, encontra a diferença e aplica o ajuste na tela real. É engenhoso, e o React deixou isso rápido. Mas toda essa dança existe para responder a uma pergunta que outra arquitetura nem precisa fazer: o que mudou?
Ryan Carniato fez a pergunta melhor. E se o código simplesmente soubesse o que mudou no instante em que mudou, e atualizasse só aquele ponto? Ele passou uma década construindo a resposta, batizou de SolidJS e, em 2026, o resto da indústria admitiu em silêncio que ele estava certo. Os Signals — a ideia no centro do Solid — estão entrando no próprio JavaScript.
Esta é a história de como “não comparar nós” saiu da heresia para virar padrão.
A falácia do Virtual DOM
O Virtual DOM fez uma aposta razoável. Re-renderiza o componente inteiro a cada mudança, compara a árvore nova com a antiga e aplica só o que difere. Em troca, você ganha um modelo mental limpo: sua interface é uma função do seu estado, e você nunca toca no DOM na mão. Esse modelo é boa parte do motivo de o React ter vencido, e merece o crédito.
Mas a aposta tem um custo. Para achar o que mudou, o motor primeiro precisa reconferir tudo o que não mudou. Numa tela movimentada a sessenta quadros por segundo, isso significa queimar ciclos re-renderizando e recomparando nós que nunca iam sair do lugar.
O Solid fez a aposta oposta, chamada reatividade fina. Em vez de reconstruir uma árvore e compará-la, o Solid liga cada pedaço de estado diretamente ao único nó do DOM que ele controla. Mude o estado, e aquele nó se atualiza. Não há árvore para percorrer, nem diff para calcular, nem re-renderização.
import { createSignal } from "solid-js";function Counter() { const [count, setCount] = createSignal(0); // Este componente roda UMA vez. Só o nó de texto se atualiza. return <button onClick={() => setCount(count() + 1)}>{count()}</button>;}Leia o comentário de novo, porque ele é a revolução inteira em uma linha: o componente roda uma vez. O React chama a função do seu componente de novo a cada render. O Solid a chama uma única vez, monta a fiação e, daí em diante, os signals fazem o trabalho.
Uma breve história da reatividade
Ajuda ver onde isso se encaixa. A reatividade no front-end passou por três eras, e cada uma trocou uma dor por outra.
Rápido e direto — mas o “callback hell” crescia junto com a aplicação.
Previsível e declarativo — mas pesado em memória e CPU pelo diffing constante.
Atualizações finas em tempo quase constante — previsível E rápido.
Cada era resolveu o problema da anterior e criou o seu próprio. Os Signals foram os primeiros a recusar a troca.
A primeira era era rápida e emaranhada. A segunda desemaranhou o código e pagou em memória. Os Signals são o primeiro modelo a recusar a troca — você ganha a clareza declarativa da era do Virtual DOM sem precisar re-renderizar o mundo para isso.
A anatomia de um signal
Reduza o Solid ao osso e você encontra três primitivas. Aprenda estas três e você aprendeu o framework.
Um Signal é a origem do estado. Você lê, você escreve — e, esta é a parte que importa, ele rastreia sozinho quem o lê. Sem arrays de dependência.
Um Memo (computed) é um valor derivado. É preguiçoso e puro: recalcula só quando algo de que depende muda, e só quando alguém de fato o lê.
Um Effect é o efeito colateral. É o único lugar com permissão para tocar o mundo externo — o DOM, o console, a rede — e re-executa apenas quando suas entradas mudam.
Rastreamento automático de quem lê, sem arrays de dependência.
Memoização pura e preguiçosa (lazy): só recalcula quando lido e quando muda.
Atualização cirúrgica direta no DOM, apenas quando as entradas mudam.
As três primitivas de uma arquitetura signal-first. O componente roda apenas uma vez.
import { createSignal, createMemo, createEffect } from "solid-js";const [preco, setPreco] = createSignal(100); // Signal: a origemconst comImposto = createMemo(() => preco() * 1.1); // Memo: derivado, lazycreateEffect(() => { console.log("Total:", comImposto()); // Effect: o efeito colateral});// Sem arrays de dependência. O Solid rastreia o que você leu.Aquele último comentário é o superpoder silencioso, e ele merece a própria seção.
O fim do array de dependências
Se você escreve React, conhece o imposto. O useEffect e o useMemo te obrigam a listar as dependências na mão:
// React: você mantém o array de dependências na mãouseEffect(() => { sync(count, query);}, [count, query]); // esqueça um item e nasce um valor velho ou um update perdidoEsqueça um item e você sobe um bug — um closure velho, uma atualização perdida, um render que nunca dispara. É um dos bugs mais comuns do React moderno, e existe porque o framework não consegue enxergar quais valores você de fato usou.
O Solid consegue. Seus efeitos observam o que você lê enquanto rodam, e o array desaparece:
// Solid: o runtime detecta o que foi usadocreateEffect(() => { sync(count(), query()); // sem array, sem bug});Segure essa ideia. Daqui a pouco ela deixa de ser um recurso do Solid e vira um recurso do JavaScript.
O custo físico
Argumento de performance é fácil de descartar, então olhe para o mecanismo, não para o marketing. A pergunta é como o custo de uma atualização cresce conforme sua aplicação cresce.
Arquitetura | Custo de Atualização | Complexidade | Necessidade de Limpeza Manual |
|---|---|---|---|
Store (NgRx) | O(n) | Trabalho proporcional ao número de assinantes | Sim |
Observables (RxJS) | O(n²) | Pode crescer quadraticamente com cadeias longas | Sim, para evitar vazamentos |
Signals (SolidJS) | O(1) | Atualização se propaga apenas para computações exatas | Não |
Com uma store centralizada (o padrão NgRx), uma atualização tende a custar cerca de O(n) — trabalho proporcional ao número de assinantes. Com longas cadeias de observables (o padrão RxJS), pode subir para O(n²), e cada subscrição é algo que você precisa lembrar de encerrar, ou ela vaza. Com signals, uma atualização fica perto de O(1) — a mudança se propaga para as computações exatas que a leram, e nada mais.
Memória cresce de forma linear. Subscrições exigem limpeza manual.
“Memory churn” de cadeias aninhadas. Subscrições exigem limpeza manual.
Memória plana ou sublinear. Limpeza automática: zero vazamentos esquecidos.
Como o custo de uma atualização cresce — e quem precisa de limpeza manual.
O resultado prático é o tipo de vitória sem graça que o usuário de fato sente: menos quedas de quadro, menos uso de GPU e nenhum vazamento lento de subscrições esquecidas. Os Signals evitam o “memory churn” que cadeias de observables aninhados criam, e se limpam sozinhos quando seu dono é destruído.
Um ecossistema pronto para produção
Nada disso importa se o framework é um projeto de fim de semana, então seja claro sobre o tamanho dele. O Solid passa de 1,5 milhão de downloads semanais e lidera as pesquisas de satisfação do State of JS ano após ano. Seu criador, Ryan Carniato, hoje é engenheiro principal de open source na Netlify e refina esse mesmo modelo reativo há mais de uma década. Essa longevidade é rara, e aparece: o núcleo do Solid é uma filosofia, não uma moda.
A pilha em volta está completa:
Isomórfico: file-based routing, server functions, SSR nativo, HTML streaming e dados sobre a Fetch API.
De createGeolocation e createIntersectionObserver a debounce e throttle.
Componentes acessíveis (WAI-ARIA) que gerenciam foco e teclado. Você traz o seu CSS, zero lock-in.
Valida um campo em tempo real sem re-renderizar o resto do formulário.
De bibliotecas mantidas pela comunidade a um meta-framework isomórfico.
Além dos Signals: o capítulo assíncrono
Aqui é onde a maioria das análises envelhece. Elas tratam os signals como linha de chegada. O Carniato os trata como capítulo um.
Os Signals resolveram a reatividade síncrona — o fluxo momento a momento do estado para a tela. O próximo problema difícil é a reatividade assíncrona: dados que chegam ao longo do tempo, estados de carregamento, condições de corrida, toda a bagunça do fetch. O SolidJS 2.0, em beta ao longo de 2026, torna o assíncrono cidadão de primeira classe. Uma computação pode retornar uma Promise, e o grafo reativo cuida sozinho da suspensão e da retomada. Você entrega uma promise direto a um memo. O Suspense foi refeito para gerenciar só a prontidão inicial, e o batching agora é determinístico.
// Solid 2.0: async é cidadão de primeira classeconst user = createAsync(() => getUser(props.id));return <h1>{user()?.name}</h1>;// O grafo reativo cuida da suspensão e da retomada por você.Essa é a parte que a turma do “signals acabaram” não vê. O modelo que começou rastreando um número agora está aprendendo a rastrear o tempo.
A guerra termina na linguagem
Agora afaste-se e veja onde todo mundo foi parar.
O Vapor Mode do Vue remove o Virtual DOM. O Svelte 5 trocou a mágica do compilador por runes — reatividade explícita, com cara de signal. O Angular reconstruiu sua detecção de mudanças sobre signals e tornou o zoneless o caminho. Três sintaxes diferentes, todas orbitando a mesma ideia que o Solid entregou anos antes.
Mas a notícia de verdade não é que copiaram em paralelo. É que estão colaborando para padronizar. Os mantenedores de Angular, Vue, Svelte, Solid, Preact, Qwik, MobX, Ember, RxJS e mais trabalham juntos numa proposta do TC39 para adicionar signals ao próprio JavaScript. Ela chegou ao Stage 1 e é modelada de propósito no esforço Promises/A+, que alinhou o ecossistema antes de o TC39 padronizar as Promises no ES2015. Já existe um polyfill.
// A proposta do TC39: signals na linguagem, sem frameworkconst count = new Signal.State(0);const double = new Signal.Computed(() => count.get() * 2);count.set(1);double.get(); // 2O argumento que venceu
Uma década atrás, “não comparar o DOM” soava como manobra — um truque esperto para uma biblioteca de nicho com seguidores apaixonados. Hoje é a direção padrão do campo inteiro, e está sendo escrito na linguagem sobre a qual todo framework é construído.
O Solid não entregou só uma biblioteca de interface rápida. Ele defendeu, com paciência e por dez anos, que o Virtual DOM era um desvio, e que a tela deveria atualizar exatamente o que mudou e nada mais. Uma pessoa segurou essa linha tempo suficiente para o resto da indústria chegar ao mesmo lugar. O argumento venceu.
Então, da próxima vez que um framework te disser que ficou mais rápido, faça a pergunta melhor — a que o Carniato fez primeiro. Mais rápido em quê? Se a resposta for “mais rápido para reconferir as partes que nunca mudaram”, você já sabe que existia outro caminho.
Escrito em junho de 2026. As versões e propostas citadas — o async de primeira classe do SolidJS 2.0 Beta, o SolidStart e a proposta de Signals no TC39 (Stage 1) — refletem o estado do ecossistema front-end nessa data. As comparações de performance descrevem o comportamento arquitetural de cada modelo, não um benchmark específico.


