Reactの仮想DOMとdiffアルゴリズムの詳細な説明

Reactの仮想DOMとdiffアルゴリズムの詳細な説明

仮想DOMの役割

まず、仮想 DOM の出現によってどのような問題が解決されるのかを知る必要があります。仮想 DOM は、頻繁に行われる直接的な DOM 操作の効率が低い問題を解決します。では、DOM を直接操作するのはなぜ非効率的なのでしょうか?

たとえば、div を作成すると、この div には多くのプロパティがあり、それを継承していることをコンソールで確認できます。特に、js を使用して DOM を操作する場合、DOM 自体は非常に複雑で、js 操作に多くの時間がかかりますが、DOM 要素自体を制御することはできません。そのため、仮想 DOM はjs が DOM を操作する問題を解決し、実際に dom 操作の数を減らします。

仮想DOMのシンプルな実装

仮想 DOM は、その名前が示すように、偽の DOM です。実際の DOM はページにマウントされますが、仮想 DOM はメモリ内にあります。これには、実際の DOM をオブジェクトに抽象化し、それをメモリに保存する必要があります。このオブジェクトは次のタイプになります。

var要素 = {
      タグ名: 'div',
      小道具: {
        クラス: 'ボックス'
      },
      子供たち: {
        {
          タグ名: 'p',
          小道具: {
            クラス: 'p1'
          },
          子供達: ['私はp1です']
        }, 
         {
          タグ名: 'p',
          小道具: {
            クラス: 'p2'
          },
          子供たち: [「私はp2です」]
        }, 
        {
          タグ名: 'p',
          小道具: {
            クラス: 'p3'
          },
          子供たち: [「私はp3です」]
        },
      }
    }

このようなオブジェクトを構築したい場合は、次のようにコンストラクターをカプセル化できます。

関数 Element(タグ名, props, children) {
    this.tagName = タグ名
    this.props = 小道具
    this.children = 子供
}

このオブジェクトを使用して、この仮想 DOM を実際の DOM にレンダリングする必要があります。次のメソッドを記述できます。

Element.prototype.render = 関数 () {
    const { tagName, props, children } = this
    var el = document.createElement(タグ名)
    for (props のキー) {
        el.setAttribute(キー、props[キー])
    }
    children.forEach((item) => {
        const childEl = (item instanceof Element) ?
              アイテム.レンダリング():
        document.createTextNode(アイテム)
        el.appendChild(childEl)
    })
    エルを返す
}

最後に、このオブジェクトを作成し、render() メソッドを呼び出して、body に appendChild を追加します。

virtualDom = new Element('div', { class: 'box' }, [ とする
    新しい要素('p', { クラス: 'p1' }, ['私はp1です']),
    新しい要素('p', { クラス: 'p2' }, ['私はp2です']),
    新しい要素('p', { クラス: 'p3' }, ['私はp3です']),
])

a = virtualDom.render() とします。
ドキュメント本体に子要素を追加します。

差分アルゴリズム

まず、diffアルゴリズムの役割を理解しましょう

仮想 DOM が変更されると、メモリ内に新しい仮想 DOM が生成されます。この新しい仮想 DOM 構造を直接使用すると、レンダリングが何度も繰り返されることになります。そのため、このときに diff アルゴリズムの役割が反映されます。diff は、新しい仮想 DOM ツリーと古い仮想 DOM ツリーを比較し、違いを見つけて記録し、記録された違いを実際の DOM ツリーに適用します。

原理:

diff アルゴリズムは、新しいツリーと古いツリーの深さ優先走査を実行し、各ノードに一意の識別子を追加します。

このプロセスは2つのステップに分かれています

  • 2 つのツリー間の違いを見つけて、疑似配列に記録します。
  • これらの違いを実際のDOMツリーに適用する

DOMの操作は基本的に4つの種類に分けられます

  • ノードの削除、移動、子ノードの追加
  • ノードラベルを変更する
  • テキストノードの場合は、ノードテキストを変更します
  • ノードプロパティを変更する

以下は、このプロセスを擬似コードの形で大まかに説明します。

// diff関数、2つのツリーを比較する function diff(oldTree, newTree) {
    var patches = {}; // 疑似配列、差異を記録 // 4種類のノードに対してエラー判定を行う dfWork(oldTree, newTree, patches, index)

    パッチを返す

}
関数 dfWork(oldTree, newTree, パッチ, インデックス) {
    現在のパッチを [] にする

    if (1) { // ノードを削除する currentPatch.push()
    } else if (3) { // ノードのテキストを変更する currentPatch.push()

    } else { // ノードのプロパティを変更します。子を確認します // プロパティに対して diff アルゴリズムを実行し、変更をパッチに記録します。
        currentPatch.push({ type: patch.PROPS, props: propsPatches })
        // 次に、子ノードに対して diff アルゴリズムを実行する必要があります diffChildren(oldNode.children, newNode.children, index, patches, currentPatch)
    }
}
関数 diffChildren(oldChildren, newChildren, index, patches, currentPatch) {

    // 子ノードに対して diff アルゴリズムを実行し、子ノードを走査し、dfWork を再帰的に呼び出して差分を取り、パッチを取得します

}
// 実際のDOMツリーに変更を適用する function patch(node, patches) {
    // ノードは古い DOM ツリーであり、変更をパッチします。
    // パッチを走査し、ノードをパッチに一致させます。
}
関数applyPatch(ノード、パッチ) {
    // 各ノードには複数の変更がある可能性があるため、switch (patchs.type) {を走査する必要もあります。
        case REPLACE: // ノードの置換 // node.render() 
            壊す;
        case REORDER: // 子ノードを移動、削除し、新しい子ノードを追加します。

            壊す;
        ケースのPROPS:
            // プロパティを設定する
            壊す;
        case TEXT: // ノードテキストを変更する // node.nodeValue
            壊す;

        デフォルト:
            壊す;
    }
}

参考資料: 詳細な分析: 仮想 DOM アルゴリズムの実装方法 著者: livoras、組み込みソース コード。

これで、React の仮想 DOM と diff アルゴリズムに関するこの記事は終了です。React の仮想 DOM と diff アルゴリズムに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。

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

<<:  nginxとバックエンドポート間の競合の解決策

>>:  MySQL の binlog_format モードと設定の詳細な分析

推薦する

MySQL は information_schema オブジェクトの付与をバイパスし、ERROR 1044 (4200) エラーを報告します

この質問は、MySQL の権限に関する WeChat グループのネットユーザー間の議論です。次のよう...

vue ルーティング ビュー router-view のネストされたジャンプの実装

目次1. app.vueページを修正する2. ログインページを作成する (/views/login/...

曇り空のアイコン効果を実現する純粋な CSS

効果効果は以下のとおりです​実装のアイデアbox-shadow プロパティを使用して、複数の灰色の円...

MySQLフィールド定義でnullを使用しない理由の分析

NULL が頻繁に使用されるのはなぜですか? (1)Javaのnull Java の NullPoi...

vscode で Prettier Code プラグインを使用する詳細なチュートリアル

なぜprettierを使うのですか?大企業では、フロントエンド開発コードに独自のコード標準がある場合...

MySQL の暗号化と復号化の例

MySQL の暗号化と復号化の例データの暗号化と復号化はセキュリティ分野で非常に重要です。プログラマ...

JavaScriptのonclickとclickの違いの詳細な説明

目次addEventListener が必要な理由は何ですか? addEventListener を...

MySQL データ型 DECIMAL(N,M) における N と M の意味の詳細な説明

同僚から、MySQL データ型 DECIMAL(N,M) の N と M の意味を尋ねられました。言...

モバイルフロントエンド適応ソリューション(概要)

ネットで検索してみたところ、多くの面接でモバイル適応方法について質問されることが分かりました。最近い...

小さなプログラムが天井に張り付いてしまう問題を完璧に解決するためにposition:stickyを使用する方法

最近、あるプロジェクトのクライアントが、上部に 2 つのタブ メニューを配置することを要求しました。...

JavaScript タイピングゲーム

この記事では、タイピングゲームを実装するためのJavaScriptの具体的なコードを参考までに紹介し...

HTML ベースタグ target=_parent の使用の紹介

<base> タグは、ページ上のすべてのリンクのデフォルトのアドレスまたはデフォルトのタ...

Webデザインの経験:ナビゲーションシステムをシンプルにする

<br />友人と話し合っていたとき、フレームワークのレイヤー設計の中で最も核となるのは...

レスポンシブWebデザイン学習(1) - 画面サイズと使用率の決定

最近では、モバイルデバイスがますます普及しており、ユーザーがスマートフォンやタブレットを使用して W...