DOMと仮想DOM

DOMと仮想DOMについて再入門したくなったので、Reactが仮想DOMをどう扱っているかを深堀りしてみることにした。

仮想DOMとは

仮想DOMはメモリ上で実際のDOMとは異なるDOMを持ち、検出した差分だけ実際のDOMに反映させるという仕組みを持つ。

www.commte.co.jp

Reactの差分検出アルゴリズムはどのような動きをするか?

Reactでは差分検出は React Fiber という差分検出エンジンによって行われる。

 legacy.reactjs.org

React Fiberとは

重要用語: Reconciliation

Reconciliationとは、Current Tree(現在の仮想DOM)とWorkInProgress Tree(新しい仮想DOM)2つのTreeを再起的に比較して差分を計算し、実際のDOMに必要な更新を行うプロセスのことを指します。

要は仮想DOMを木構造の差分アルゴリズムによって検出している。

React Fiberとは

図で分かるReact18のしくみ

木構造の差分検出は主にどのようにやるのか?

React15 以前は Stack reconciler というアルゴリズム(あまり一般的ではない?)で実装されており、16以降は Stack reconciler を書き直した reconciler アルゴリズム(Fiber reconcilerと呼ばれる)で実装されている。

完全な Fiber reconciler のアルゴリズムは以下のサイトが参考になりそうだ。

調べてみると意外に試してみている人もいる。

An Introduction to React Fiber - The Algorithm Behind React

15以前の Stack reconciler の実装であれば以下記事で公式がフルスクラッチでの作成手順を公開している。

 ja.legacy.reactjs.org

  1. reconciler 関数に渡された変数の型によって以下のどちらかを実行してDOMをrenderする
    • 再帰的に <App/> のようなユーザ定義コンポーネントをrenderする関数
    • div,p のようなhost要素をrenderするための関数
  2. ompositeComponent と DOMComponent という上記関数を含むクラスにそれぞれリファクタ。
  3. ライフサイクルメソッド用の関数をクラスメソッド内で実行されるようにする。

差分処理については 更新 という章で説明されている。

※ 詳細まで理解、整理できたら追記する。

 ja.legacy.reactjs.org

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 (
        // クリーンアップ処理
    );
});

projects.wojtekmaj.pl