Vue 仮想 DOM クイックスタート

Vue 仮想 DOM クイックスタート

仮想DOM

仮想DOMとは何か

DOM は、ドキュメントをノード ツリーの形式で表現するドキュメント オブジェクト モデルです。

仮想 DOM は実際の DOM ではありません。むしろ、それは JavaScript オブジェクトです。

通常の DOM ノードは HTML では次のように表されます。

<div クラス = 'testId'>
    <p>こんにちは</p>
    <p>ようこそ</p>
</div>

仮想 DOM では次のようになります。

{
    タグ: 'div',
    属性:
        クラス: ['testId']
    },
    子供たち:[
        // p 要素 // p 要素]
}

理解しやすいように、仮想 DOM を仮想 + DOM の 2 つの部分に分割できます。

  • 仮想: 仮想 DOM は実際の DOM ではなく、JavaScript オブジェクトであることを意味します。
  • dom: 仮想 dom がドキュメントをノード ツリーのような形式で表現できることを示します。

仮想DOMの役割

現在主流となっているフレームワークはすべて、宣言型 DOM 操作用のフレームワークです。状態と DOM 間のマッピング関係を記述するだけで済みます。フレームワークは、状態をビュー (実際の DOM) に変換するのに役立ちます。

最も単純なアプローチは、状態をビューにレンダリングし、状態が更新されるたびにビュー全体を更新することです。

このアプローチのパフォーマンスは想像できます。より良いアイデアは、状態が変化すると、その状態に関連する DOM ノードのみが更新されることです。仮想 DOM はこのアイデアを実装する 1 つの方法にすぎません。

具体的な手順:

  • 状態 -> 実ドメイン(初期)
  • 状態 -> 仮想 DOM -> 実際の DOM (仮想 DOM を使用)

状態が変化すると、新しい仮想 DOM が生成され、以前のものと現在のものを比較して更新が必要な部分を見つけ、実際の DOM が更新されます。

Vue の仮想 DOM

実際の DOM はノード (Node) で構成され、仮想 DOM は仮想ノード (vNode) で構成されます。

Vue では、仮想 DOM は主に次の 2 つのことを行います。

  • 実ノード(Node)に対応する仮想ノード(vNode)を提供する
  • 新しい仮想ノードと古い仮想ノードを比較し、違いを見つけてビューを更新します。

「仮想 DOM」は、Vue コンポーネント ツリーによって構築された VNode ツリー全体の名前です - vue 公式サイト

vノード

vNodeとは

前述の通り、vNode(仮想ノード)は実ノード(Node)に相当します。

vNode はノード記述オブジェクトとして理解できます。実際のDOMノードを作成する方法を説明します

vue.js には vNode クラスがあります。さまざまなタイプの vNode インスタンスを作成するために使用できます。さまざまなタイプの vNode は、さまざまなタイプの DOM 要素に対応します。コードは次のとおりです。

デフォルトクラスVNodeをエクスポートする{
   コンストラクタ(
    タグ?: 文字列、
    データ?: VNodeData、
    子?: ?Array<VNode>,
    テキスト?: 文字列、
    elm?: ノード、
    コンテキスト?: コンポーネント、
    コンポーネントオプション?: VNodeComponentOptions、
    asyncFactory?: 関数
  ){
    this.tag = タグ
    this.data = データ
    this.children = 子供
    this.text = テキスト
    this.elm = エルム
    this.ns = 未定義
    this.context = コンテキスト
    this.fnContext = 未定義
    this.fnOptions = 未定義
    this.fnScopeId = 未定義
    this.key = データ && データ.key
    this.componentOptions = コンポーネントオプション
    this.componentInstance = 未定義
    this.parent = 未定義
    this.raw = 偽
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = 非同期ファクトリー
    this.asyncMeta = 未定義
    this.isAsyncPlaceholder = false
  }

  子を取得():コンポーネント | void {
    this.componentInstance を返す
  }
}

vNode クラスによって作成されたインスタンスが本質的に通常の JavaScript オブジェクトであることは、コードから簡単にわかります。

vNode の種類

vNode クラスを通じてさまざまな種類の vNode を作成できることはすでに紹介しました。異なるタイプの vNode は、有効な属性によって区別されます。たとえば、isComment = true はコメント ノードを示し、isCloned = true はクローン ノードを示します。

vNode タイプには、コメント ノード、テキスト ノード、クローン ノード、要素ノード、コンポーネント ノードが含まれます。

コメント ノード、テキスト ノード、クローン ノードのコードは次のとおりです。

/*
コメント ノードの有効な属性: {isComment: true、テキスト: 'コメント ノード'}
*/
エクスポートconst createEmptyVNode = (テキスト: 文字列 = '') => {
  定数ノード = 新しい VNode()
  node.text = テキスト
  // コメント node.isComment = true
  戻りノード
}
/*
テキスト ノードの有効な属性: {text: 'text node'}
*/
エクスポート関数createTextVNode (値: 文字列 | 数値) {
  新しい VNode(undefined, undefined, undefined, String(val)) を返します
}

// 最適化された浅いクローン
// 静的ノードとスロットノードに使用されます。
// 静的ノードとスロットノードの場合 // 複数のレンダリングでは、それらを複製することで、DOM操作が
// elm 参照に基づきます。
// クローンノードエクスポート関数 cloneVNode (vnode: VNode): VNode {
  const クローン = 新しい VNode(
    vnode.タグ、
    vnode.data、
    //#7975
    // 複製の際に元の配列が変更されないように、子配列を複製します
    // 子供。
    vnode.children && vnode.children.slice(),
    vnode.text、
    vnode.elm、
    vnode.コンテキスト、
    vnode.componentOptions、
    vnode.asyncファクトリ
  )
  クローン.ns = vnode.ns
  クローンされた.isStatic = vnode.isStatic
  クローンされたキー = vnode.key
  クローンされた.isComment = vnode.isComment
  クローンされたfnContext = vnode.fnContext
  クローンされたfnOptions = vnode.fnOptions
  クローンされたfnScopeId = vnode.fnScopeId
  クローンされたasyncMeta = vnode.asyncMeta
  // ノードをクローンとしてマークします。isCloned = true
  クローンを返す
}

ノードを複製すると、実際には既存のノードのすべてのプロパティが新しいノードに割り当てられ、最終的にcloned.isCloned = trueで複製されたノードとしてマークされます。

要素ノードには通常、次の 4 つの属性があります。

  • タグ: ノード名。たとえば、div、p
  • data: ノード上のデータ。たとえば、クラス、スタイル
  • children: 子ノード
  • コンテキスト: コンポーネント内でレンダリングされる

コンポーネント ノードは要素ノードに似ており、次の 2 つの固有のプロパティが含まれます。

  • componentOptions: propsData、リスナー、子、タグなどのコンポーネント ノードのオプション パラメータ
  • componentInstance: コンポーネントのインスタンス

パッチ

Vue で仮想 DOM が行う最初のことはすでに紹介しました。実際のノード (Node) に対応する仮想ノード (vNode) を提供することです。次に、2 番目のことを紹介します。新しい仮想ノードを古い仮想ノードと比較し、違いを見つけて、ビューを更新します。

Vue で実装されている 2 つ目のものは、パッチ適用または修復を意味する patch と呼ばれます。古い vNode と新しい vNode を比較し、違いを見つけて、既存の DOM に基づいてパッチを適用することで、ビューが更新されます。

vNode を比較して違いを見つけることが手段であり、ビューを更新することが目的です。

ビューを更新するということは、ノードの追加、ノードの削除、ノードの更新を行うということに他なりません。次に、ノードをいつ追加するか、どこに追加するか、ノードをいつ削除するか、どのノードを削除するか、ノードをいつ更新するか、どのノードを更新するかを 1 つずつ分析します。

注意: vNode と oldVNode が異なる場合は、vNode が優先されます。

新しいノードの追加

1 つの状況は、vNode は存在するが oldVNode が存在しない場合、新しいノードを追加する必要があることです。最も典型的なのは、odlVNode が存在しないための初期レンダリングです。

もう 1 つのケースは、vNode と oldVNode がまったく同じノードではない場合です。このとき、vNode を使用して実際の DOM ノードを生成し、oldVNode が指す実際の DOM ノードの隣に挿入する必要があります。 oldVNode は放棄されたノードです。たとえば、次のような状況です。

<div>
  <p v-if="type === 'A'">
    私はノードAです
  </p>
  <span v-else-if="type === 'B'">
    私はAとは全く異なるノードBです
  </span>
</div>

タイプが A から B に変更されると、ノードは p から span に変更されます。vNode と oldVNode はまったく同じノードではないため、新しいノードを追加する必要があります。

ノードの削除

ノードが oldVNode にのみ存在する場合は、それを削除します。

ノードの更新

前のセクションでは、新しいノードの追加とノードの削除のシナリオを紹介しました。これらには共通点が 1 つあります。vNode は oldVNode とはまったく異なるということです。

しかし、より一般的なシナリオは、vNode と oldVNode が同じノードである場合です。次に、vNode と oldVNode 間のより詳細な比較を行い、oldVNode に対応する実際のノードを更新する必要があります。

テキストノードの場合、ロジックは当然単純です。まず、古い vNode と新しい vNode を比較して、同じノードであることを確認します。次に、oldVNode に対応する dom ノードのテキストを vNode のテキストに変更します。しかし、インターフェース内のツリー コンポーネントなどの複雑な vNode の場合、このプロセスは複雑になります。

新しいノード - ソースコード分析

考えてみてください。前述したように、vNode の種類は、コメント ノード、テキスト ノード、クローン ノード、要素ノード、コンポーネント ノードです。これらすべての型が作成され、DOM に挿入されますか?

回答: コメント ノード、テキスト ノード、要素ノードのみ。 HTML はこれらのタイプのみを認識するためです。

ノードは上記の3種類しかないので、種類に応じて対応するノードを作成し、対応する位置に挿入します。

要素ノードを例にとると、vNode に tag 属性がある場合、それは要素ノードであることを意味します。 createElement メソッドを呼び出して対応するノードを作成し、次に appendChild メソッドを使用してそれを指定された親ノードに挿入します。親要素がすでに表示されている場合は、その下に要素を挿入すると自動的にレンダリングされます。vNode の isComment プロパティが true の場合は、コメント ノードを示します。どちらも true でない場合は、テキスト ノードを示します。

通常、要素には子ノードが存在するため、ここでは再帰的なプロセスが実行されます。つまり、vNode 内の子ノードを 1 つずつ走査し、ノードを作成してから、それらを親ノード (親ノードは、作成したばかりの dom ノード) に、レイヤーごとに再帰的に挿入します。

ソースコードをご覧ください:

// 要素を作成する function createElm (
  vノード、
  挿入されたVnodeQueue、
  親エルム、
  refElm、
  ネストされた、
  所有者配列、
  索引
){
  if (isDef(vnode.elm) && isDef(ownerArray)) {
    // この vnode は以前のレンダリングで使用されました。
    // 今は新しいノードとして使用されているので、elmを上書きすると
    // 挿入として使用した場合、将来的にパッチエラーが発生する可能性があります
    // 参照ノード。代わりに、作成する前にオンデマンドでノードを複製します。
    // それに関連付けられた DOM 要素。
    vnode = ownerArray[インデックス] = cloneVNode(vnode);
  }

  vnode.isRootInsert = !nested; // 遷移のチェックに入る
  コンポーネントを作成する場合(vnode、挿入されたVnodeQueue、親Elm、refElm) {
    戻る
  }

  var データ = vnode.data;
  var children = vnode.children;
  var タグ = vnode.tag;
  // タグ属性を持ち、要素ノードであることを示す if (isDef(tag)) {
    vnode.elm = vnode.ns
      ? nodeOps.createElementNS(vnode.ns、タグ)
      // 要素を作成します。 nodeOps はクロスプラットフォームを伴います: nodeOps.createElement(tag, vnode);
    スコープを設定します(vnode);

    /* イスタンブールは無視します */
    {
      // 子ノードを再帰的に作成し、親ノードに挿入します。createChildren(vnode, children, insertedVnodeQueue);
      if (isDef(データ)) {
        フックの作成を呼び出します(vnode、挿入されたVnodeQueue);
      }
      //vnode に対応する要素を親要素に挿入します insert(parentElm, vnode.elm, refElm);
    }

  // isCommentプロパティはコメントノードを示します} else if (isTrue(vnode.isComment)) {
    vnode.elm = nodeOps.createComment(vnode.text);
    //親ノードを挿入insert(parentElm, vnode.elm, refElm);
  // それ以外の場合は子ノードです} else {
    vnode.elm は nodeOps.createTextNode(vnode.text);
    //親ノードを挿入insert(parentElm, vnode.elm, refElm);
  }
}

// 子ノードを再帰的に作成し、親ノードに挿入します。 vnodeは親ノードを表します。関数createChildren(vnode, children, insertedVnodeQueue){
  Array.isArray(children) の場合
    process.env.NODE_ENV !== 'production' の場合 {
      checkDuplicateKeys(子);
    }
    // 子ノードを一つずつ作成し、親ノードに挿入します for (var i = 0; i < children.length; ++i) {
      createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i);
    }
  } そうでない場合 (isPrimitive(vnode.text)) {
    nodeOps.appendChild(vnode.elm、nodeOps.createTextNode(String(vnode.text)));
  }
}

ノードの削除 - ソースコード分析

ノードの削除は非常に簡単です。ソースコードを直接見てみましょう:

// 指定されたノードのセットを削除する function removeVnodes (parentElm, vnodes, startIdx, endIdx) {
  (; startIdx <= endIdx; ++startIdx) の場合 {
    var ch = vnodes[startIdx];
    if (isDef(ch)) {
      if (isDef(ch.tag)) {
        削除フックを削除して呼び出します(ch);
        DestroyHook を呼び出します(ch);
      } else { // テキストノード
        // ノードを削除しますremoveNode(ch.elm);
      }
    }
  }
}

// 単一のノードを削除する function removeNode (el) {
  var 親 = nodeOps.parentNode(el);
  // 要素は v-html / v-text によりすでに削除されている可能性があります
  if (isDef(親)) {
    // nodeOps はクロスプラットフォーム メソッド nodeOps.removeChild(parent, el); をカプセル化します。
  }
}

以上がVue Virtual DOMクイックスタートの詳しい内容です。Vue Virtual DOMの詳細については、123WORDPRESS.COMの他の関連記事もご覧ください。

以下もご興味があるかもしれません:
  • この記事は、Vueの仮想Domとdiffアルゴリズムを理解するのに役立ちます。
  • Vue仮想DOMの原理
  • Vue 仮想 DOM の問題について
  • Vueソースコード解析における仮想DOMの詳しい説明
  • Vueにおける仮想DOMの理解のまとめ
  • Vue 仮想 Dom から実際の Dom への変換
  • Vue の仮想 DOM に関する知識ポイントのまとめ

<<:  MySQL の FIND_IN_SET() と IN の違いを簡単に分析します

>>:  CentOS に Memcached と PHP Memcached 拡張機能をインストールする

推薦する

Vueは画像のドラッグと並べ替えを実装します

この記事の例では、画像のドラッグと並べ替えを実装するためのVueの具体的なコードを参考までに共有して...

Vueベースのカスタムコンポーネントを実装してアイコンを導入する

序文プロジェクト開発では、アイコンを使用する方法はたくさんあります。iconfont で適切なアイコ...

ウェブページ作成時のHTMLタグの使用に注意してください

この記事では、Web ページの作成を学習するときに注意すべき HTML タグに関するいくつかの問題を...

html mailto(メール)の実用化について

ご存知のとおり、mailto は Web デザインと制作において非常に実用的な HTML タグです。...

Keras を使って SQL インジェクション攻撃を判断する (例の説明)

この記事では、ディープラーニングフレームワーク keras を使用して、SQL インジェクションの特...

Mysql は、デッドロック問題を解決するために kill コマンドを使用します (実行中の特定の SQL ステートメントを強制終了します)。

MySQL を使用して特定のステートメントを実行すると、データ量が多いためにデッドロックが発生し、...

Nginx フォワード プロキシとリバース プロキシ、および負荷分散機能の構成コード例

この記事は主に、Nginx のフォワード プロキシとリバース プロキシ、および負荷分散機能の設定コー...

Vue3 でタイマーコンポーネントをカプセル化する方法

背景一部のショッピング モールの Web ページで商品の詳細を開くと、購入数量を選択するためのカウン...

MySQL ルートパスワードを変更する複数の方法 (推奨)

方法1: SET PASSWORDコマンドを使用する MySQL -u ルート mysql> ...

シンプルなカルーセル効果を実現するネイティブ js

この記事では、シンプルなカルーセル効果を実現するためのjsの具体的なコードを参考までに紹介します。具...

MySQL 8.0.24 のインストールと設定方法のグラフィックチュートリアル

この記事では、MySQL 8.0.24のインストールチュートリアルを参考までに紹介します。具体的な内...

画像のフェードインとフェードアウト効果を実現する js

この記事では、画像のフェードインとフェードアウトを実現するためのjsの具体的なコードを参考までに紹介...

divは、自動入力スタイルをブロックする入力ボックスとして入力を使用せずにコンテンツを入力できます。

今日、私は公開用の動的なウィンドウ スタイルを設計しましたが、マウスで入力をクリックしたときにブラウ...

動的な色切り替えの実装コードをサポートするために、CSS で SVG 画像を参照します。

表示する svg 画像を追加すると、React はファイルが見つからないというメッセージを表示します...

MySQL データベースは SQL ステートメントを知っている必要があります (拡張バージョン)

拡張版です。質問とSQL文は以下の通りです。ユーザー テーブルを作成し、id、name、gender...