Virtual DOM and Signals

How Virtual DOM Diffing and Signals Work

Why Frameworks Exist contrasted the two models; here is the machinery. A virtual DOM renderer re-runs the component to get a new tree of plain objects and diffs it against the old one. A general tree diff is O(n³); React 7,897 's reconciler is O(n) thanks to two heuristics. An element whose type changed (<div> to <section>, or one component to another) is rebuilt, losing its state. Children are matched by key, or by position without keys, so inserting at the top of an unkeyed list rewrites every row: the classic answer to "why does React need keys?" Vue 3 5,482 's compiler adds hints: static nodes are cached, and patch flags such as 2 /* CLASS */ tell the diff what may change.

Signals skip the diff: a signal records which running computation reads it and re-runs only those.

A minimal signal and effect, the core of Solid-style reactivityJavaScript
let running = null;  // the effect that is executing right now
function signal(value) {
  const subscribers = new Set();
  const read = () => { if (running) subscribers.add(running); return value; };
  const write = (v) => { if (v !== value) { value = v; subscribers.forEach((f) => f()); } };
  return [read, write];
}
function effect(fn) {
  const run = () => { const outer = running; running = run; fn(); running = outer; };
  run();
}
const [count, setCount] = signal(0), [user, setUser] = signal('Ada');
effect(() => console.log(`text node = ${count()}`));  // roughly what Solid makes of {count()}
effect(() => console.log(`title attribute = ${user()}`));
setCount(1); setCount(1); setUser('Grace');
Output
text node = 0
title attribute = Ada
text node = 1
title attribute = Grace

The repeated setCount(1) did nothing, and setUser never touched the count. A Solid 269,718 component runs once; then effects like these patch single nodes. Built-in signals are a Stage 1 TC39 proposal (github.com/tc39/proposal-signals (https://github.com/tc39/proposal-signals 4,191 )).