ReactアプリケーションにおけるDOM DIFFアルゴリズムの詳細な説明

ReactアプリケーションにおけるDOM DIFFアルゴリズムの詳細な説明

序文

フロントエンド開発に携わる人にとって、最も人気のあるフロントエンドフレームワークは間違いなくReactとVueです。この2つのフレームワークは誰もがよく知っていると思います。ただし、これら 2 つのフレームワークはいずれも従来の DOM テクノロジの使用を放棄し、仮想 DOM とも呼ばれる JS ベースの仮想 DOM テクノロジを採用しています。では、仮想 DOM とは何でしょうか? 2 つの一般的なフレームワークの両方が仮想 DOM を使用する理由は何ですか?次に、フロントエンド開発者の私が、DOM DIFF アルゴリズムの素晴らしさについて説明します。

仮想 DOM とは何ですか?

Virtual DOM は、その名の通り仮想的な DOM です。Javascript を使用して DOM 構造をシミュレートし、JS レイヤーで DOM の変化を比較する機能が特徴です。具体的な操作は以下のとおりです。

// 通常の HTML DOM 構造 <ul class="list">
   <li class="item">わい</li>
   <li class="item">易陽千熙</li>
</ul>

// マッピングされた仮想DOM
{
   タグ:'ul',
   小道具:{
       クラス:'リスト'
   },
   子供たち: [
       {
           タグ:'li',
           小道具:{
               クラス:'アイテム'
           },
           子供達: ['易楊千熙']
       }
   ]
}

ちょっと戸惑う人もいるかもしれません。普通の HTML DOM 構造はダメなんじゃないの?理解しやすく、コードも簡潔になります。ネストされた再帰仮想 DOM を使用する必要があるのはなぜでしょうか?実は、これは通常の DOM が再レンダリングの過程で多くのパフォーマンスを消費するという事実に関係しています。DOM 操作は単純に見えますが、実際には効率はかなり低いです。これは、頻繁に変更する必要があるのが実際の DOM の場合、より複雑に見え、JS 構造を使用する仮想 DOM の方が効率的になるためです。

仮想DOMを使用する理由

DOMレンダリングページ操作プロセス

  • ブラウザがドメイン名を通じてサーバーから対応する HTML ファイルを取得すると、ブラウザはまず DOM ツリーと CSSOM ツリーを構築します。ツリーの概念に関しては、データ構造とアルゴリズムを学んだ学生はツリー ノードの概念に感銘を受けるかもしれません。
  • HTML DOM ではすべてがノードであり、DOM はノード ツリーとして表示される HTML であり、各ノードには対応する階層関係があります。 DOMノードツリーはHTMLのタグに一つずつ対応し、DOMツリーを形成します。
  • HTML ファイル

  • HTML DOMツリー

同じ。 CSS ドキュメントでは、すべての要素はノードであり、HTML のタグに 1 対 1 で対応し、以下に示すように CSSOM ツリーを形成します。

警告します! DOM ツリーの構築中に JS 関連のコンテンツに遭遇すると、DOM ツリーの構築は直ちに停止します。これは、JS が DOM ノード上で操作できるためです。完成した DOM に JS が影響しないように、ブラウザは DOM ツリーの構築を防止してリソースを節約します。

DOM ツリーと CSSOM ツリーが継続的に構築されるにつれて、レンダリング ツリーも徐々に形成されます。ブラウザーは、構築されたレンダリング ツリーに基づいて Web ページのレイアウトと描画処理を実行し、Web ページを継続的に構築します。具体的な操作フローチャートは以下のとおりです。

一般的に、ページ レンダリングの日常的な操作では、DOM を操作し、innerHTML を変更してリセットすることで、ページ レンダリングを完了します。DOM 更新操作を実行するたびに、レンダリング プロセスが再度実行され、このプロセスにはページの再描画と再配置が含まれます。

仮想DOMの利点

しかし、大規模なページ プロジェクトや、タグや属性が多数ある Web ページの場合、従来の DOM 操作では時間がかかりすぎます。単純な変更を行うたびに、多数の DOM ノードの再描画と再配置が必要になり、ページ レンダリングの効率が大幅に低下します。そのため、フロントエンド開発者が DOM のボトルネックに直面して途方に暮れている場合、仮想 DOM は軽量の JavaScript オブジェクトとして大きな優位性を発揮し、フロントエンド開発者の支持を獲得することに成功しています。

ページが再レンダリングされると、Virtual DOM は DOM の diff 計算と比較を 2 回実行し、差異を見つけます。DOM ツリーの異なる部分を変更するだけで済みます。Virtual DOM はミドルウェアであるとも言えます。まず、JS を使用して Virtual DOM を変更します。差異を比較した後、すべての変更がページの実際の DOM に追加されます。したがって、Virtual DOM の最大の利点は、ネイティブ DOM のように比較後に DOM を再構築して作成する必要がないことです。これは、大規模なプロジェクトの操作に非常にパフォーマンスを消費し、コストがかかるためです。Web ページのサイズに関係なく、従来の DOM を放棄して Virtual DOM を採用することは、間違いなく非常に効率的で優れた選択であることがわかります。

仮想DOMでDOMを表現する方法

まず、vscodeで新しいdom diffプロジェクトを作成し、プロジェクトを初期化し、対応するコンポーネントをインストールします。

DOM ツリーなので、HTML を DOM ツリーに変換する際には再帰形式を使います。まずノードを作成し、次に属性を設定し、最後に子ノードを設定します。

<ul class="リスト">
    <li class="item">わい</li>
    <li class="item">易陽千熙</li>
</ul> 

// DOMツリー式変換フォーム let virtualDOM = createElement('ul', {
  クラス:'リスト',
}, [
  要素を作成します('li',{
    クラス:'アイテム'
  },['wjy']),
  要素を作成します('li',{
    クラス:'アイテム'
  },['易楊千熙']),
])

次に、新しいelement.jsファイルを作成して外部に出力し、ページのレンダリングを完了します。

// ElementコンストラクタクラスElementを通じて仮想DOMノードを構築します。
    コンストラクタ(型、プロパティ、子){
        this.type = タイプ;
        プロパティ
        this.children =子供;
    }
}
// 
const createElement = (型、プロパティ、子) => { 
  新しい要素(type,props,children)を返します。
}

// ページをレンダリングし、仮想DOMを実際のDOMに変換します
const レンダリング = (domObj) => {  
    el = document.createElement(domObj.type); とします。
    for(let key in domObj.props){
        setAttr(el,key,domObj.props[key]);
    }
    domObj.children.forEach(child => {
        child = (要素の子インスタンス)
        ? レンダリング(子)
        : document.createTextNode(子);
        el.appendChild(子);

    })
    el を返します。
}

関数setAttr(ノード、キー、値){
    スイッチ(キー){
        ケース '値':
            if(node.tagName.toLowerCase() === '入力' || 
            node.tagName.toLowerCase() === 'テキストエリア'
            ){
                ノードの値 = 値;
            }それ以外{
                node.setAttribute(キー、値)
            }
            壊す;
            ケース 'スタイル':
            // node.setAttribute('style',値)
            node.style.cssText の値;
            壊す;
            デフォルト:
                node.setAttribute(キー、値)
            壊す;
    }
}

// 実際のDOMを指定されたルートノードにマウントします。const renderDOM = (el,target) => {
    ターゲットに子要素を追加します。
}

//外部にエクスポート
    要素を作成、
    与える、
    レンダリングDOM
}

コンソールで実際のDOMを取得する

ページのレンダリングに成功しました! 😃

DOM DIFFアルゴリズム

ユーザーがインタラクティブなページ操作を変更すると、仮想 DOM ツリー上のノードは変更されますが、この時点では実際のノードは変更されません。変更を実際のページと同期させるために、DOM DIFF アルゴリズムを使用して 2 つのツリーの差異を検出し、差異パッチ オブジェクトを生成してから、差異パッチ オブジェクトを実際の DOM ノードに適用して、ページのレンダリングと更新を完了します。

従来の Diff アルゴリズムの時間計算量は O(n^3) に達します。ページ全体を毎回更新するという目的を達成するには、この指数関数的に増大するパフォーマンス オーバーヘッドではパフォーマンス要件を満たすことができません。そのため、Facebook のエンジニアはこれを最適化し、diff 戦略を策定して Diff アルゴリズムの計算量を O(n) に削減しました。

異なる戦略

  • DOM ノードに対するレベル間操作はほとんどないため、無視できます。
  • 同じクラスの 2 つのコンポーネントは類似のツリー構造を生成し、異なるクラスの 2 つのコンポーネントは異なるツリー構造を生成します。
  • 同じレベルの子ノードのグループ。UUIDによって区別できます。

差分粒度

DIFF の粒度が異なるため、DIFF アルゴリズムは次の順序で実行されます。

  • ツリーDIFF
  • コンポーネント DIFF
  • 要素の違い

パッチ適用

React diff の diff 戦略と比較アルゴリズムに従って、深さ優先トラバーサルを通じて 2 つの仮想 DOM を比較します。違いがある場合は、トラバースされたノードのインデックス値に対応する操作 (パッチ オブジェクトとも呼ばれます) が保存されます。

次に、実際の DOM が深さ優先で再度トラバースされ、パッチ オブジェクト内のインデックスが DOM に対応し、DOM 更新操作が完了します。

結論 インターネット環境では、インタラクティブなページをいつでも更新することは、インターネットを閲覧するときに日常的に行われる操作です。ただし、この単純な操作は、複数のアルゴリズムの最適化の結果です。 DOM DIFF の基本的な原理は非常に複雑です。ご興味があれば、関連する文献をご自身で検索してください。この記事は表面的な分析に過ぎないため、あまり多くの側面については詳しく説明しません。この記事に誤りや抜けがある場合は、ご訂正ください。あらゆる合理的な批判を謙虚に受け入れましょう! 😉

この記事が DOM diff アルゴリズムの理解に役立った場合は、ぜひ高評価をお願いします。私はフロントエンド開発の初心者です。高評価をいただくたびに、前進するモチベーションになります。Nuggets のブログは今後も更新していきます。

上記は、React アプリケーションにおける DOM DIFF アルゴリズムの詳細な説明です。React アプリケーションにおける DOM DIFF アルゴリズムの詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • React の調整アルゴリズム Diffing アルゴリズム戦略の詳細な説明
  • Reactのdiffアルゴリズムの詳細な分析
  • Reactの仮想DOMとdiffアルゴリズムの詳細な説明
  • React diffアルゴリズムソースコード分析
  • ReactレンダリングプロセスからのDiffアルゴリズムの分析に関する簡単な説明
  • React diffアルゴリズムの実装例
  • React の Diff アルゴリズムをご存知ですか?

<<:  Docker を使用して ELK ログ システムを構築する例

>>:  MySQL 5.7.15 のインストールと設定方法のグラフィック チュートリアル (Windows)

推薦する

Dockerコンテナがホストポートにアクセスできない場合の解決策

最近、仕事中に問題が発生しました。Docker コンテナがホストの redis にアクセスできず、t...

Docker データボリュームコンテナの作成と使用状況分析

データ ボリューム コンテナーは、データ ボリュームをマウントするために特別に使用されるコンテナーで...

ホストサービスにアクセスするDockerでのサービスの実装

目次1. シナリオ2. 解決策3. 結論4. 参考文献1. シナリオ日常の開発およびテスト作業には ...

RedisとMemcacheの比較と選び方

最近 redis を使っていて、とても便利だと感じているのですが、インメモリ データベースを選択する...

HTML ページ スタイルの !-- -- の機能は何ですか?

主に低バージョンのブラウザ向け<!-- --> は HTML コメント タグです。上位バ...

VUE ユニアプリテンプレート構文についての簡単な説明

1.v-bind(略称:)コンポーネント プロパティのデータで定義されたデータ変数を使用するか、コン...

MYSQL ストアドプロシージャと関数の簡単な記述

ストアドプロシージャとは簡単に言えば、これは強力で、JAVA 言語のメソッドに似た比較的複雑な論理関...

MySQLの大規模テーブル最適化ソリューションについての簡単な説明

背景Alibaba Cloud RDS for MySQL(MySQL バージョン 5.7)データベ...

MySQL 8.0.18 安定版がリリースされました! 予想通りハッシュ結合が実装されました

MySQL 8.0.18 安定版 (GA) が昨日正式にリリースされ、Hash Join も期待通り...

html-cssタグのスタイル設定が機能しない2つの理由

1 セミコロン「;」のない CSS スタイル2 タグが閉じられておらず、「>」がありません...

MySQLバックアップとリカバリの実践に関する詳細な説明

1. mysqlbackup の紹介mysqlbackup は、MySQL Enterprise B...

CMDコマンドを使用してMySqlデータベースを操作する方法の詳細な説明

まず、mysqlサービスを開始および停止します ネットストップmysql ネットスタートMySQL ...

Navicat が MySql サーバーにリモート接続できない問題の解決策

Navicat が MySql サーバーにリモート接続できない問題の解決策は、先頭に書かれています:...

CSS 完全な視差スクロール効果

1. 何ですか視差スクロールとは、複数の背景レイヤーを異なる速度で動かすことで、3次元のモーション...

Linux システムで MySQL データベースにリモート接続する方法のチュートリアル

序文最近、職場でこの要件に遭遇し、リモート接続を確立するのに 1 時間以上かかりました。ローカル コ...