DOMと仮想DOMについて再入門したくなったので、Reactが仮想DOMをどう扱っているかを深堀りしてみることにした。
仮想DOMとは
仮想DOMはメモリ上で実際のDOMとは異なるDOMを持ち、検出した差分だけ実際のDOMに反映させるという仕組みを持つ。
Reactの差分検出アルゴリズムはどのような動きをするか?
Reactでは差分検出は React Fiber という差分検出エンジンによって行われる。
React Fiberとは
重要用語: Reconciliation
Reconciliation
とは、Current Tree
(現在の仮想DOM)とWorkInProgress Tree
(新しい仮想DOM)2つのTreeを再起的に比較して差分を計算し、実際のDOMに必要な更新を行うプロセスのことを指します。
要は仮想DOMを木構造の差分アルゴリズムによって検出している。
木構造の差分検出は主にどのようにやるのか?
React15 以前は Stack reconciler というアルゴリズム(あまり一般的ではない?)で実装されており、16以降は Stack reconciler を書き直した reconciler アルゴリズム(Fiber reconcilerと呼ばれる)で実装されている。
完全な Fiber reconciler のアルゴリズムは以下のサイトが参考になりそうだ。
調べてみると意外に試してみている人もいる。
An Introduction to React Fiber - The Algorithm Behind React
15以前の Stack reconciler の実装であれば以下記事で公式がフルスクラッチでの作成手順を公開している。
- reconciler 関数に渡された変数の型によって以下のどちらかを実行してDOMをrenderする
ompositeComponent
とDOMComponent
という上記関数を含むクラスにそれぞれリファクタ。- ライフサイクルメソッド用の関数をクラスメソッド内で実行されるようにする。
差分処理については 更新 という章で説明されている。
※ 詳細まで理解、整理できたら追記する。
Reactのライフサイクルメソッドと生DOMを更新するタイミング
実際のDOMが更新されるタイミングは Mount → Update → UnMount と3つある。
DOMが更新されると以下のライフサイクルメソッドが呼ばれる。
- componentDidMount
- componentDidUpdate
- componentWillUnmount
上記メソッドは、現在では Hook を使って上記ライフサイクルメソッドを以下のように useEffect
代替することができる。(上記仮想DOMとDOM更新の関係性を理解するとHookという名前はジャストネーミングな気がする)
componentDidMount
useEffect( () => {
// 初回のみ実行する処理
}, []);
componentDidUpdate
useEffect( () => {
// 毎回実行する処理
});
componentWillUnmount
componentDidUpdate を実行する useEffect の中で定義しているため、以下コードの場合 componentDidUpdate も行われることになる。
あくまでも return();
の中が componentWillUnmount に対応する。
useEffect( () => {
// 毎回行う処理
return (
// クリーンアップ処理
);
});