← PC・IT用語集へ戻る

仮想DOM

Virtual DOM
programming web beginner
実際のDOM(Web画面の構造)をメモリ上に複製し、変更された部分だけを効率よく反映させる仕組み。
仮想DOM (Virtual DOM)

概要(サマリー)

仮想DOM(バーチャルDOM)は、ブラウザがWebページを描画するためのツリー構造(DOM)を、パソコンのメモリ上に「仮のコピー(軽量な複製)」として作り、画面の更新を効率よく高速に行うための仕組みである。

React や Vue.js などのモダンなフロントエンドフレームワークの裏側で採用されている。データが変わった際に、まずメモリ上の仮想DOM同士を比較して「本当に書き換える必要がある最小限の差分」を計算し、その差分だけを実際のブラウザの画面(リアルDOM)に反映させることで、ページの描画パフォーマンスを劇的に向上させる。


詳細解説

仮想DOMとは何か(メモリ上に作る仮の画面構造)

ブラウザがHTMLを読み込んで画面を表示する際、内部ではHTMLのタグをツリー状のデータ構造に変換している。これが「DOM(ドキュメントオブジェクトモデル)」である。

JavaScript を使って画面の一部を書き換える場合、このDOMを直接操作する。しかし、DOMは非常に巨大で複雑なオブジェクトであるため、JavaScriptで直接何度も書き換え(DOM操作)を行うと、ブラウザの再描画処理(レンダリング)が追いつかなくなり、動作が重くなる。

仮想DOMは、このブラウザの「リアルDOM」とは別に、「JavaScriptのオブジェクトとして記述された、軽くてシンプルな仮のDOM構造」をメモリ上に作成し、操作のクッションとして挟む技術である。

なぜ仮想DOMが必要なのか(描画コストの削減)

ブラウザにとって、画面を「再描画」する作業は非常にコスト(計算量と時間)がかかる処理である。

たとえば、100件の商品リストがあるWebページにおいて、1件の商品名だけを更新したいとする。

  • 従来の直接DOM操作(命令的)
  • 単純な実装では、100件のリスト全体のHTMLを組み立て直し、丸ごとブラウザのDOMに上書きしてしまう。
  • ブラウザは「リスト全体が変わった」と判断し、変わっていない99件も含めてレイアウトを再計算し、画面全体を描き直すため、非常に動作が重くなる。
  • 仮想DOMによるアプローチ(宣言的)
  • まず、変更後のデータに基づいて、メモリ上の「新しい仮想DOM」を作成する。
  • メモリ上で「変更前の古い仮想DOM」と「新しい仮想DOM」を比較し、「3件目のリストの商品名テキストだけが書き換わった」という最小限の差分(Diff)を見つける。
  • その「3件目のテキストの書き換え」というピンポイントの指示だけを実際のブラウザのDOMに送信する。

このように、ブラウザにとって最も重い処理である「実際の画面書き換え」を必要最小限(差分のみ)に抑えることができるため、複雑なWebアプリでもサクサク動くようになる。

仮想DOMが画面を高速に書き換える仕組み(差分検出)

仮想DOMの動作プロセスは、大きく分けて以下の3ステップで実行される。

  1. 仮想DOMの生成:プログラム内で状態(データ)が変更されると、Reactは新しい仮想DOMツリーを作成する。JavaScriptのメモリ上で作られるため、この生成処理自体は一瞬で完了する。
  2. 差分比較(Diffing / Reconciliation):古い仮想DOMツリーと、新しく作った仮想DOMツリーを比較し、変更された部分を特定する。Reactはこの比較を非常に高速に行うアルゴリズム(調停 / リコンシリエーション)を持っている。
  3. 一括反映(Patching / バッチ処理):特定された差分データだけを、実際のブラウザのDOMに適用する。この際、複数の変更をまとめて1回で反映させるため、ブラウザの再描画回数が最小限に抑えられる。

以下は、JavaScriptによる直接DOM操作と、React(仮想DOM)による記述の対比パターンである。

悪い例:JavaScriptによる直接DOM操作(毎回画面全体を書き換えてしまう非効率な例)

// ボタンを押すたびに、HTML文字列全体を構築し直してinnerHTMLで直接DOMを書き換える
function updateList(items) {
  const listElement = document.getElementById('item-list');
  let html = '';
  items.forEach(item => {
    html += `<li>${item.name}</li>`;
  });
  listElement.innerHTML = html; // リスト全体が毎回破壊され、再描画される
}

良い例:Reactによる宣言的UI(仮想DOMが差分だけを検知して効率よく更新する例)

import React from 'react';

// データ構造を記述しておくだけで、React(仮想DOM)が差分だけを自動更新する
function ItemList({ items }) {
  return (
    <ul id="item-list">
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

Reactのコンポーネントでは、データ(items)が変わると、仮想DOMが自動的に key 属性(item.id)を頼りに「どの項目が増えたか・変わったか」を検出し、その <li> タグだけをピンポイントで書き換える。


AIコーディングとの関係

仮想DOMを効率よく動かすコードを書くためには、仮想DOMの仕組み(特に key 属性の役割)を理解してAIに指示を出す必要がある。

正確なリスト要素の key 属性設定の指示

仮想DOMが差分比較を行う際、配列要素の「並び順」や「個々の識別」を行うために key という目印(属性)が使われる。この key に配列のインデックスindex 番号)をそのまま指定すると、並び替えや削除の際に仮想DOMが誤作動し、描画バグや速度低下の原因になる。

  • 指示の例:「Reactのリスト表示コンポーネントを生成してください。map 関数ループで使用する key には、配列の index を使うのではなく、各データが持つ一意な id(一意の識別子)を明示的に指定するように実装してください。」

直接的なDOM操作(document.getElementById 等)の排除

Reactプロジェクト内で、AIが直接ブラウザのDOMを触る命令(document.querySelectorelement.classList.add など)を出力することがある。これはReactの仮想DOMが管理する状態と実際のDOMの状態に「ズレ」を引き起こす原因になるため、AIに指示してReactの機能(useRefstate)を使うように修正させる必要がある。

  • 指示の例:「Reactのプロジェクトですので、直接的なDOM操作(document.getElementById など)は使用しないでください。代わりに useRef を使って要素の参照を取得し、状態管理(state)によってスタイルや表示状態を切り替える実装に書き直してください。」

よくある勘違い

仮想DOMを使えばどんな画面でも必ず速くなる?

「仮想DOMは魔法の技術なので、生のJavaScript(バニラJS)で書くよりも常に画面表示が速くなる」というのは誤解である。

もし、「テキストが1箇所だけ変わる」といった非常にシンプルで静的なWebページであれば、生のJavaScriptで element.textContent = '新テキスト' と1行書く方が、仮想DOMを作成して比較する手間がない分、圧倒的に高速で消費メモリも少ない。

仮想DOMが真価を発揮するのは、「SNSのタイムラインやチャットアプリ、ダッシュボードのように、画面内の複雑な階層データが頻繁に、多方向から書き換わるモダンなWebアプリケーション」においてである。複雑なデータ更新を人間が手作業で最適化するコードを書くのは不可能なため、仮想DOMに一任した方がはるかに高速で安全になる。

仮想DOMはブラウザに標準搭載されている機能?

「仮想DOMは、Google ChromeやSafariなどのブラウザが標準で持っている機能である」と思っている人がいるが、これは誤りである。

仮想DOMは、ブラウザの機能ではなく、ReactやVue.jsといった「JavaScriptライブラリが自分たちのコードの一部として実装しているソフトウェアの仕組み」である。

そのため、仮想DOMを動作させるには、ライブラリのプログラム(Reactの本体コードなど)を事前にブラウザに読み込ませて実行する必要がある。

仮想DOMを使うならHTMLやDOMの知識は不要?

「Reactの仮想DOMが全部処理してくれるなら、HTMLの構造(DOM)やブラウザの描画の仕組みは知らなくてよい」というのは誤りである。

最終的にユーザーが見る画面は、ブラウザが処理した「本物のDOM(リアルDOM)」である。

仮想DOMがいくら効率的に差分を計算しても、最終的に出力されるHTMLタグの構造が不自然(無駄なネストが多い、不適切な要素選択など)であれば、ブラウザ側での画面描画(ペイント処理)が遅くなり、結果的に重いサイトになってしまう。パフォーマンスの高いアプリを作るためには、リアルDOMのレンダリングプロセス(CSSの適用やリフローの仕組み)の知識を学んでおくことが依然として重要である。


まとめ

  • 仮想DOMは、実際のDOMの軽量なコピーをメモリ上に作成し、状態の変更処理を高速に行う仕組み。
  • 変更前後の仮想DOMを比較して「差分」のみを検出し、実際のDOMへ一括反映することで描画コストを削減する。
  • リスト表示の差分検出を正しく行うには、各要素に一意な key 属性を設定することが極めて重要。
  • AIコーディングの際は、直接DOM操作をしないように指示し、Reactの「状態管理」と「仮想DOM」のルールに沿ったコードを出力させる。

情報ソース


より詳しくAIに聞いてみよう

  • 仮想DOMによる「差分比較(リコンシリエーション)」は、具体的にどのようなアルゴリズムでツリーの差分を高速に検出していますか?
  • Reactのリスト表示において、なぜ key 属性に配列の index を使ってはいけないのか、具体的なバグの発生パターンを教えてください。
  • 仮想DOMを使用する React と、仮想DOMを使用せずにコンパイル時に効率的なDOM操作コードを生成する Svelte や SolidJS などのフレームワークのアプローチの違いを比較してください。
  • AIコーディングでReactのコンポーネントを実装してもらう際、不要な再描画(レンダリング)が発生してパフォーマンスが低下するのを防ぐための指示方法(useMemouseCallback の指定)を教えてください。
  • ブラウザが本物のDOMを受け取ってから、実際にピクセルとして画面に描画(Reflow, Repaint)するまでのステップをわかりやすく説明してください。