Blog Article
React の仮想 DOM を基礎から理解する

はじめに
React を学習していると、「仮想 DOM(Virtual DOM)」という言葉を必ず目にします。
JavaScript では DOM を直接操作して HTML 要素を取得し、アニメーションを付けたり、内容を書き換えたりできますが、「仮想 DOM が何なのか」「なぜ必要なのか」が分からず、なんとなく使っている人も多いのではないでしょうか。
この記事では、プログラミング初学者の方でも理解できるように、仮想 DOM の仕組みを基礎から丁寧に説明します。
さらに、React が高速に画面を更新できる理由である diff と patch、レンダリングの流れ、仮想 DOM がいつ・どのように更新されるのかまで、順を追って解説していきます。
参考にしたリンク
- https://react.dev/learn/render-and-commit
- https://react.dev/reference/react
- https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model
仮想 DOM とは何か
仮想 DOM とは、「ブラウザに表示されている本物の DOM を直接操作せず、まずメモリ上に仮想的な DOM を作って更新する仕組み」のことです。
一言で言うなら、「画面の設計図をメモリ上に持っておき、差分だけを本物の画面に反映するための仕組み」です。
React は、画面を更新するたびに直接 DOM を触るのではなく、「今どんな UI を表示したいか」という情報を JavaScript のオブジェクトとして保持します。このオブジェクトの集合が、一般に仮想 DOM と呼ばれています。
仮想 DOM が必要な理由
仮想 DOM が必要な理由を一言で表すなら、「DOM 操作を最小限に抑えるため」です。
もし仮想 DOM がなかった場合、React は state が変わるたびに「どこを更新すべきか」を開発者が自分で管理する必要があります。
これはコードが複雑になり、バグの原因にもなります。
仮想 DOM があることで、開発者は「どう表示したいか」だけを書けばよく、更新の最適化は React が自動で行ってくれます。
なぜ本物の DOM を直接操作しないのか
ブラウザの DOM は、画面に直接影響を与える存在です。そのため、DOM を変更すると、ブラウザは次のような処理を行います。
- レイアウトの再計算
- スタイルの再適用
- 再描画
これらはすべてコストの高い処理です。要素が多い画面で DOM 操作を頻繁に行うと、動作が重くなり、カクつきや遅延の原因になります。
仮想 DOM は何が違うのか
仮想 DOM は、ただの JavaScript オブジェクトです。
メモリ上のデータなので、更新や比較が非常に高速です。
React はまずこの軽いデータ構造を更新し、前回の状態と比較して「どこが変わったのか」だけを調べます。そして、必要最小限の変更だけを本物の DOM に反映します。
仮想 DOM の基本的な流れ
仮想 DOM を使った React の画面更新は、次のような流れで行われます。
あなたのコード
↓
仮想 DOM を生成・更新
↓
前回の仮想 DOM と比較(diff)
↓
必要な部分だけを実 DOM に反映(patch)この仕組みによって、React は無駄な DOM 操作を避けながら、効率よく UI を更新できます。
diff と patch とは何か
diff とは、「前回の仮想 DOM と新しい仮想 DOM を比較し、どこが変わったのかを特定する処理」です。
一言で言うなら、「変更点を見つける作業」です。
React は仮想 DOM を木構造として扱い、タグの種類、属性、テキスト、子要素などを順番に比較していきます。
patch とは、diff によって見つかった差分を「実際の DOM に反映する処理」です。
diff が「何が変わったか」を調べるのに対し、patch は「その変更を画面に反映する」役割を担います。
diff と patch は必ずセットで動きます。
- diff:変更点を洗い出す
- patch:その変更点だけを実 DOM に適用する
この分業によって、React は高速な画面更新を実現しています。
React の diff アルゴリズム
React はすべての要素を完全に比較しているわけではありません。
現実的な速度を保つために、いくつかのルールを設けています。
- 同じ型の要素は同じものとして扱う
<div>が<div>のままであれば、React は「同じ要素」と判断し、中身だけを比較します。一方で、
<div>が<span>に変わると、要素そのものが別物とみなされ、作り直されます。 - 子要素は基本的に順番で比較する
React はリスト要素を位置ベースで比較します。
そのため、並び順が変わると不要な再生成が起きやすくなります。
- key がある場合は key で比較する
keyを指定すると、React は「同じ key を持つ要素は同一」と判断します。これにより、リストの並び替えでも正しく差分を計算できます。
diff の具体的なイメージ
たとえば、次のような変更があったとします。
前の仮想 DOM:
<h1>こんにちは</h1>新しい仮想 DOM:
<h1>こんばんは</h1>diff の結果として、React は次のように判断します。
<h1>要素は同じ- テキストだけが変更されている
そのため patch では、<h1> のテキスト部分だけが書き換えられます。
仮想 DOM はいつ更新されるのか
仮想 DOM は、「再レンダーが発生したとき」に更新されます。
React では、次のような場合に再レンダーが起こります。
- state が更新されたとき
- props が変更されたとき
再レンダーとは、「新しい UI の説明書を作り直すこと」です。
レンダリングフェーズとコミットフェーズ
React の画面更新は、次の 2 つのフェーズに分かれています。
フェーズ | 内容 |
|---|---|
Render フェーズ | 新しい仮想 DOM を作る |
Commit フェーズ | 差分を実 DOM に反映する |
仮想 DOM が作られるのは Render フェーズです。その後に diff と patch が行われます。
React は前回の仮想 DOM を保持している?
React は内部に Fiber と呼ばれるデータ構造を持っており、前回のレンダー結果を保持しています。これが「前回の仮想 DOM」に相当します。
差分を計算するためには、「前」と「後」の状態が必要です。そのため、React は常に前回の情報を保持しています。
実 DOM はあくまで描画結果であり、React にとっての本当の状態は Fiber ツリーです。
仮想 DOM を作るために必要な情報
- コンポーネントの state
- 親から渡された props
- JSX による UI の定義
これらをもとに、「今この瞬間の UI」を表す仮想 DOM が作られます。
diff を行うために必要な情報
- 前回の仮想 DOM
- 今回の仮想 DOM
- 差分を計算するルール
これらを使って、React は最小限の更新箇所を特定します。
まとめ
- 仮想 DOM は UI の状態を表す軽量なデータ構造です
- 本物の DOM を直接操作せず、差分だけを反映します
- diff は変更点を見つける処理です
- patch は差分を実 DOM に適用する処理です
- 仮想 DOM は再レンダー時に必ず作り直されます
仮想 DOM の仕組みを理解すると、React が「なぜ速いのか」「なぜ書きやすいのか」がはっきり見えてきます。
内部の動きを意識しながらコードを書くことで、より良いコンポーネント設計やパフォーマンス改善につなげることができるでしょう。