Programação & Dev

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.

O fim do Virtual DOM: por que os Signals do SolidJS garantem performance O(1) sem 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.

Funcionamento do Virtual DOM nova vs antiga aplica diferenças Reconstrução da árvore do componente Re-renderiza componente inteiro a cada mudança Comparação entre árvore nova e antiga Identifica diferenças para atualizar só o necessário Aplicação das mudanças no DOM real Atualiza o DOM real com as diferenças detectadas
Funcionamento do Virtual DOM

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.

Fluxo da reatividade fina no SolidJS liga a dispara cria define mantém Estado (Signal) Pedaço de estado individual Ligação direta Estado ligado ao nó do DOM Atualização no nó DOM Só o nó afetado é atualizado Componente monta uma vez Função executada só na montagem Sem re-renderização Não há reexecução da função do componente
Fluxo da reatividade fina no SolidJS
JSX
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.

Três eras de reatividade
Callbacks
2005–2012

Rápido e direto — mas o “callback hell” crescia junto com a aplicação.

Virtual DOM
2013–2018

Previsível e declarativo — mas pesado em memória e CPU pelo diffing constante.

Signals
2019–hoje

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.

Signal, Computed e Effect
Origem do estado
Signal

Rastreamento automático de quem lê, sem arrays de dependência.

Valor derivado
Computed

Memoização pura e preguiçosa (lazy): só recalcula quando lido e quando muda.

Efeito colateral
Effect

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.

JSX
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:

JSX
// 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 perdido

Esqueç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:

JSX
// 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.

O duelo das arquiteturas
~O(n)
Store (NgRx)

Memória cresce de forma linear. Subscrições exigem limpeza manual.

~O(n²)
Observables (RxJS)

“Memory churn” de cadeias aninhadas. Subscrições exigem limpeza manual.

~O(1)
Signals

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:

A fundação arquitetural
Meta-framework
SolidStart

Isomórfico: file-based routing, server functions, SSR nativo, HTML streaming e dados sobre a Fetch API.

Utilitários reativos
Solid Primitives

De createGeolocation e createIntersectionObserver a debounce e throttle.

UI headless
Kobalte & Corvu

Componentes acessíveis (WAI-ARIA) que gerenciam foco e teclado. Você traz o seu CSS, zero lock-in.

Validação cirúrgica
Modular Forms

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.

JSX
// 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.

JAVASCRIPT
// 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(); // 2

O 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.