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 モードと設定の詳細な分析

推薦する

Windows での MySQL の使用: 自動スケジュールバックアップの実装

1. バックアップスクリプトを書く 著者:www.yumi-info.com 日付:20171222...

CSS3 でテキストマーキーを実装するためのサンプルコード

背景何が起こったかというと、Luzhu は偶然、宇宙で最高の外部スピーカーを備えた携帯電話について知...

要素の$notifyポイントについての簡単な説明

当初の意図は、element-ui の $notify 通知をコンポーネントにカプセル化することでし...

Tomcat maxPostSize設定実装プロセス分析

1. maxPostSize を設定する理由は何ですか? tomcat コンテナには送信データのサイ...

Centos7でポートを開く方法

CentOS7 のデフォルトのファイアウォールは iptables ではなく、firewalle で...

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

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

1時間で学ぶMySQLの基礎

目次MySQL を使い始めるMySQL 管理6. MySQL サーバーを起動および停止します。 7....

Dockerにおけるオーバーレイネットワークの詳細な説明

Docker 公式ドキュメントからの翻訳、原文: https://docs.docker.com/n...

vue-resource インターセプターの使用に関する詳細な説明

序文インターセプター最近のフロントエンド フレームワークでは、インターセプターは基本的に非常に基本的...

Linux および CentOS (サーバー) に zip および unzip コマンド機能をインストールする

Linux に zip 解凍機能をインストールする通常、 zip コマンドは Linux サーバーに...

純粋な CSS でマークダウンの自動番号付けを実装するサンプル コード

問題の起源私がタイトルの番号付けの問題に初めて注目したのは、学部の論文を書いていた頃まで遡ります。当...

MySQL、Oracle、SQL Server のページングクエリ例の分析

最近、Oracle、MySQL、SQL Server 2005 のデータ ページング クエリについて...

MySQL でテーブルを作成するときの NULL と NOT NULL の使用方法の詳細な説明

MySQL の仕様によっては、テーブル作成仕様にすべてのフィールドが空であってはならないという要件を...

Navicat 8でMySQL用のデータベースを作成する方法

ウェブサイトを開発する場合、データを保存するためにデータベースを使用する必要があることがよくあります...

Linuxの相対パスと絶対パスの使用

01. 概要絶対パスと相対パスはシェル環境でよく使用され、それぞれに独自の用途があります。相対パスの...