cshtml+javascriptで作られたサービスをReactにリプレイスする際にDOM操作について考えさせられることが多くなったので学び直すことにしました。
DOMとは
Document Object Modelの略。
JavaScriptからHTMLドキュメントを操作するためのインターフェース、およびデータ構造。
Webブラウザは、Webサーバから受け取ったHTMLドキュメントを解析しDOMと呼ばれるデータ構造(インターフェイス)に変換する。
DOMはNodeツリーとなっている。
Node
NodeはいくつものDOMAPIオブジェクトタイプが継承しているインターフェイス。
- Document
- Element
- Attr
などがある。
Documentオブジェクト
ブラウザ固有の処理を行うためのオブジェクトの一つ。
DOM操作のためのメソッドが含まれている。
<p id="changeText">テキスト</p>
<script>
const target = document.getElementById("changeText");
target.innerHTML = "テキスト変更";
</script>
例では、document.getElementByIdで”changeText”というidを持つNodeをDOM全体から検索。取得したNodeをinnerHTMLを使って中身を書き換えている。
このようにDOMに対してAPIを使って、HTMLドキュメントを操作することができる。
「仮想DOM」と区別して「リアルDOM」と呼ぶ。
リアルDOM操作とレンダリング
WebブラウザはHTMLドキュメントをレンダリング(コンテンツをブラウザの画面に表示する処理)することで画面を描画します。4つのステップを踏む。
- HTMLの解析
- レンダリングツリーの構築(DOMツリー・CSSツリー→レンダリングツリー)
- レイアウト処理
- 描画
ブラウザはDOM操作イベントによって発火し、レンダリング処理を行う(再レンダリング)
レンダリングはブラウザにとってコストの高い処理である。
仮想DOM
仮想DOMはバーチャルのDOM(軽量なJSオブジェクト)。このバーチャルDOMはブラウザ上にはなく、メモリ上に保存されている。
差分検知
UIが変更された場合は変更後の仮想DOM構造体を作成し、メモリ上にある変更前の仮想DOM構造体と比較して、異なる部分だけを抽出してくる。
差分検知をメモリ上で素早く行い、抽出した差分をリアルDOMに適用するので、ブラウザは必要な分だけのレンダリングで済む。
React化
リアルDOMでの課題
- ソースコードの肥大で、どこでなにをしているのかが分かりづらい
- 状態をチェックすることができない
- 前回の状態に依存した処理が必要(処理が複雑化)
- レンダリングコストがかかる
Reactの特徴
1. UIとロジックの分離
リアルDOMの課題:
ソースコードの肥大で、どこでなにをしているのかが分かりづらい。
React化:
どこでなにをしているのかをJavaScript側で把握しなくてよい。
変更をするのは状態であり、「HTMLのどの要素を取得し、何をするか」は関係ない。
状態の一元管理(グローバルStateなど)でメンテナンスしやすくなる。
2. 宣言的UI
リアルDOMの課題:
状態をチェックすることができない。
前回の状態に依存した処理が必要(処理が複雑化)
React化:
状態が変化したら自動的に再描画する。
前の状態に依存せず、最終的な状態を宣言的に記述できる。
3.仮想DOM
リアルDOMの課題:
レンダリングコストがかかる
React化:
仮想DOMによってメモリ上で抽出した差分のみが再描画される。DOMへの操作が最小限。
全てを再描画する処理を行ってもパフォーマンスの低下を防げる。
仮想DOMとリアルDOMの1対1の関係性
リアルDOMの操作は仮想DOMとリアルDOMの1:1の関係性を壊す原因となる。
1:1の関係性が壊れると仮想DOMが検知した差分を、正しくリアルDOMに反映させる事ができない。
リアルDOMを操作するようなjQueryとReactの相性が悪いのはこれが理由。
参考