仮想 DOM

実際の DOM の軽量なコピーをメモリ上に保持し、差分だけを効率的に更新する仕組み

フロントエンドReact

仮想 DOM とは

仮想 DOM (Virtual DOM) は、実際の DOM の軽量な JavaScript オブジェクトのコピーをメモリ上に保持し、状態変更時に新旧の仮想 DOM を比較 (diff) して、差分だけを実際の DOM に反映する仕組みである。React が 2013 年に普及させた。

なぜ必要か

DOM 操作はブラウザで最もコストの高い処理の 1 つ。

直接 DOM 操作:
  状態変更 → DOM 全体を再構築 → レイアウト再計算 → 再描画 (遅い)

仮想 DOM:
  状態変更 → 仮想 DOM を再構築 (メモリ上、高速)
           → 新旧の仮想 DOM を比較 (diff)
           → 差分だけ実際の DOM に反映 (最小限の操作)

仮想 DOM の仕組み

// 仮想 DOM ノード (単なる JavaScript オブジェクト)
const vdom = {
  type: 'div',
  props: { className: 'card' },
  children: [
    { type: 'h1', props: {}, children: ['Hello'] },
    { type: 'p', props: {}, children: ['World'] },
  ],
};
1. 状態変更 → render() で新しい仮想 DOM ツリーを生成
2. 新旧の仮想 DOM を比較 (Reconciliation)
3. 差分を計算 (例: テキストが "Hello" → "Hi" に変更)
4. 差分だけ実際の DOM に適用 (textContent = "Hi")

React の Reconciliation アルゴリズム

React は仮想 DOM の差分を検出する際、異なる型の要素はツリー全体を再構築し、同じ型の要素は属性の差分だけを更新する。リストでは key で要素を識別し、移動・追加・削除を最小化する。

// ❌ key がないとリスト全体を再レンダリング
{items.map(item => <li>{item.name}</li>)}

// ✅ key で要素を識別し、差分だけ更新
{items.map(item => <li key={item.id}>{item.name}</li>)}

仮想 DOM を使わないフレームワーク

フレームワーク アプローチ
React 仮想 DOM + diff
Svelte コンパイル時に直接 DOM 操作コードを生成
SolidJS Fine-grained reactivity (シグナル)
Vue 仮想 DOM + コンパイラ最適化

Svelte や SolidJS は仮想 DOM を使わず、コンパイル時に最適な DOM 操作コードを生成する。仮想 DOM の diff コストがゼロになるため、理論上はより高速だ。

React Server Components と仮想 DOM

React Server Components はサーバーで仮想 DOM を生成し、シリアライズしてクライアントに送信する。クライアントでは JavaScript のバンドルサイズが削減される。

パフォーマンスの注意点

仮想 DOM は「DOM 操作を最小化する」が、「仮想 DOM の diff 自体」にもコストがある。不要な再レンダリングを防ぐために React.memouseMemouseCallback を適切に使う。

仮想 DOM の背景や設計思想は関連書籍に詳しい。

関連用語