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)

推薦する

Linux での mysql8.018 のインストールと設定のプロセスの詳細な説明

Windowsでのインストールの紹介:こちらもご覧ください –》WindowsでのMySQL 8.0...

ネイティブJSでマウススライドによる愛の拡散効果を実現

この記事では、マウスをスライドすると愛が広がる js 特殊効果を紹介します。効果は次のとおりです。 ...

MySQL 8.0 ディクショナリテーブル拡張の詳細な説明

MySQL のデータ ディクショナリは、データベースの重要なコンポーネントの 1 つです。INFOR...

完全バックアップとポイントインタイムバックアップにmysqldumpを使用する方法

Mysqldump は MySQL の論理バックアップに使用されます。高速ではありませんが、柔軟性が...

nginx のロードバランシングとリバースプロキシの説明

目次負荷分散負荷分散分類1. DNS 負荷分散2. IP負荷分散3. リンク層の負荷分散4. ハイブ...

CSS3のbox-shadowプロパティの使い方の詳細な例

CSS には多くの属性があります。特に複数の値を設定する必要がある属性は、長期間使用しないと忘れられ...

mysqlパラメータsql_safe_updatesを使用して更新/削除範囲を制限する方法の詳細な説明

序文皆さんご存知のとおり、MySQL の運用・保守において、更新/削除条件が誤っているためにデータが...

Vue codemirrorはオンラインコードコンパイラの効果を実現します

序文Web 上でオンライン コード コンパイルの効果を実現したい場合は、 CodeMirrorを再度...

Linux ファイル操作でよく使われるコマンドのまとめ

0. 新しい操作: mkdir abc #新しいフォルダを作成 touch abc.sh #新しいフ...

角丸四角形の HTML+CSS 実装コード

退屈していたので、突然角丸四角形の実装を思いつきました。しかし、私たちはこの話題についてあまりにも長...

MySQL 8.0.11 の詳細なインストール手順

この記事では、参考までにMySQL 8.0.11のインストール手順を紹介します。具体的な内容は次のと...

MySQL が外部キーを作成できない理由と解決策

2 つのテーブルを関連付けるときに、外部キーを作成できませんでした。このブログから、問題は、ポイント...

Vue+Element UIはドロップダウンメニューのカプセル化を実現します

この記事の例では、ドロップダウンメニューのカプセル化を実装するためのVue + Element UI...

Vueでファジークエリを実装する方法の簡単な例

序文いわゆるファジークエリとは、ユーザーの完全な入力やすべての入力情報がなくてもクエリサービスを提供...

MYSQLの主キー制約とユニーク制約の違いについて簡単に説明します。

目次主キー制約ユニーク制約主キー制約PRIMARY KRY 主キーは一意です。テーブルには主キーを ...