仮想DOM
Virtual DOM
概要(サマリー)
仮想DOM(バーチャルDOM)は、ブラウザがWebページを描画するためのツリー構造(DOM)に反映する前に、パソコンのメモリ上に「仮の画面構造」を作り、画面の更新内容を整理するための仕組みである。
React や Vue.js などのモダンなフロントエンドフレームワークで使われてきた考え方である。データが変わった際に、まずメモリ上の仮想DOM同士を比較して「どこを更新するか」を計算し、必要な変更を実際のブラウザの画面(リアルDOM)に反映させることで、複雑な画面更新を扱いやすくする。
詳細解説
仮想DOMとは何か(メモリ上に作る仮の画面構造)
ブラウザがHTMLを読み込んで画面を表示する際、内部ではHTMLのタグをツリー状のデータ構造に変換している。これが「DOM(ドキュメントオブジェクトモデル)」である。
JavaScript を使って画面の一部を書き換える場合、このDOMを直接操作することがある。しかし、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ステップで実行される。
- 仮想DOMの生成:プログラム内で状態(データ)が変更されると、Reactは新しい仮想DOMツリーを作成する。
- 差分比較(Diffing / Reconciliation):古い仮想DOMツリーと、新しく作った仮想DOMツリーを比較し、変更された部分を特定する。Reactはこの比較を効率よく行うアルゴリズム(調停 / リコンシリエーション)を持っている。
- 一括反映(Patching / バッチ処理):特定された差分データを、実際のブラウザのDOMに適用する。この際、複数の変更をまとめて反映させることで、不要なDOM操作を減らしやすくなる。
以下は、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(Reactが更新内容を整理して反映する例)
import React from 'react';
// データ構造を記述しておくと、Reactが更新内容を整理して反映する
function ItemList({ items }) {
return (
<ul id="item-list">
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Reactのコンポーネントでは、データ(items)が変わると、Reactが key 属性(item.id)を頼りに「どの項目が増えたか・変わったか」を判断し、必要なDOM更新を行う。
AIコーディングとの関係
仮想DOMを効率よく動かすコードを書くためには、仮想DOMの仕組み(特に key 属性の役割)を理解してAIに指示を出す必要がある。
正確なリスト要素の key 属性設定の指示
仮想DOMが差分比較を行う際、配列要素の「並び順」や「個々の識別」を行うために key という目印(属性)が使われる。この key に配列のインデックス(index 番号)をそのまま指定すると、並び替えや削除の際にReactが要素を正しく対応づけにくくなり、描画バグや速度低下の原因になることがある。
- 指示の例:「Reactのリスト表示コンポーネントを生成してください。
map関数のループで使用するkeyには、配列のindexを使うのではなく、各データが持つ一意なid(一意の識別子)を明示的に指定するように実装してください。」
直接的なDOM操作(document.getElementById 等)の排除
Reactプロジェクト内で、AIが直接ブラウザのDOMを触る命令(document.querySelector や element.classList.add など)を出力することがある。これはReactが管理する状態と実際のDOMの状態に「ズレ」を引き起こす原因になるため、AIに指示してReactの機能(useRef や state)を使うように修正させる必要がある。
- 指示の例:「Reactのプロジェクトですので、直接的なDOM操作(
document.getElementByIdなど)は使用しないでください。代わりにuseRefを使って要素の参照を取得し、状態管理(state)によってスタイルや表示状態を切り替える実装に書き直してください。」
よくある勘違い
仮想DOMを使えばどんな画面でも必ず速くなる?
「仮想DOMは魔法の技術なので、生のJavaScript(バニラJS)で書くよりも常に画面表示が速くなる」というのは誤解である。
もし、「テキストが1箇所だけ変わる」といったシンプルで静的なWebページであれば、生のJavaScriptで element.textContent = '新テキスト' と1行書く方が、仮想DOMを作成して比較する手間がない分、軽く済む場合がある。
仮想DOMが役立ちやすいのは、「SNSのタイムラインやチャットアプリ、ダッシュボードのように、画面内の複雑な階層データが頻繁に、多方向から書き換わるモダンなWebアプリケーション」である。複雑なデータ更新を人間が手作業で管理するのは難しいため、Reactのようなライブラリに更新処理を任せた方が、実装を整理しやすい。
仮想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の適用やリフローの仕組み)の知識を学んでおくことが依然として重要である。
情報ソース
- React Legacy Docs: Virtual DOM and Internals (English)
- MDN Web Docs: DOM (Document Object Model) の紹介
まとめ
- 仮想DOMは、実際のDOMへ反映する前にメモリ上の仮の構造で更新内容を整理する仕組み。
- 変更前後の仮想DOMを比較して差分を見つけ、必要なDOM更新を行いやすくする。
- リスト表示の差分検出を正しく行うには、各要素に一意な
key属性を設定することが重要。 - AIコーディングの際は、直接DOM操作を避けるように指示し、Reactの状態管理に沿ったコードを出力させる。
より詳しくAIに聞いてみよう
- 仮想DOMによる「差分比較(リコンシリエーション)」は、具体的にどのようなアルゴリズムでツリーの差分を高速に検出していますか?
- Reactのリスト表示において、なぜ
key属性に配列のindexを使ってはいけないのか、具体的なバグの発生パターンを教えてください。 - 仮想DOMを使用する React と、仮想DOMを使用せずにコンパイル時に効率的なDOM操作コードを生成する Svelte や SolidJS などのフレームワークのアプローチの違いを比較してください。
- AIコーディングでReactのコンポーネントを実装してもらう際、不要な再描画(レンダリング)が発生してパフォーマンスが低下するのを防ぐための指示方法(
useMemoやuseCallbackの指定)を教えてください。 - ブラウザが本物のDOMを受け取ってから、実際にピクセルとして画面に描画(Reflow, Repaint)するまでのステップをわかりやすく説明してください。