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 拡張機能をインストールする

推薦する

HTML でよく使われるメタ百科事典 (推奨)

メタタグは、HTML言語のヘッド領域にある補助タグです。HTML文書のヘッダーにあるヘッドタグとタイ...

MySql インデックスを表示および最適化する方法

MySQL はハッシュ インデックスと Btree インデックスをサポートしています。 InnoDB...

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

この記事では、MacOSでのMySQL 8.0.18のインストールと成功したコマンドライン操作を記録...

CSS の ::before と ::after 疑似要素について知らないこと

CSS には、一般的には使用されない 2 つの疑似クラス、before と :after があります...

Docker を使用して Nginx+Flask+Mongo アプリケーションをデプロイする

サーバーにはNginx、データベースサポートにはMongo、Python言語のWebフレームワークに...

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

この記事では、MySQL 8.0.17のインストールと設定方法を参考までに紹介します。具体的な内容は...

海外でダウンロードできる25個の新鮮で便利なアイコンセット

1. Eコマースアイコン2. アイコンスイーツ2 3. 携帯電話アイコンパック4. 旗アイコンセット...

CentOS のファイルと権限の基本操作チュートリアル

序文始める前に、ファイル属性とファイル属性を変更する方法について簡単に理解しておく必要があります。 ...

デザイン理論: デザインにおける階層

<br />原文: http://andymao.com/andy/post/80.ht...

HTML 縦列表示テキストを使用してテキストを縦列で表示します

コードをコピーコードは次のとおりです。 <span style='display:bl...

CSS3 で背景ぼかしを実現する 3 つの方法 (要約)

1. 通常の背景ぼかしコード: <スタイル> html, 体 { 幅: 100%; 高...

音声キューイングシステムを実装するためのJavaScript

目次導入主な特徴エフェクト表示キーコード導入音声キューイングシステムは、銀行、レストラン、病院などの...

HTML、CSS、JSコメントの標準的な使用法の概要

必要なコメントを追加することは、責任感と道徳心のあるフロントエンド開発者が持つべき良い習慣であり、コ...

JavaScript のクロージャの詳細な説明

導入クロージャは JavaScript の非常に強力な機能です。いわゆるクロージャは関数内の関数です...

リフレッシュリダイレクトを実現する HTML ヘッドタグメタ

コードをコピーコードは次のとおりです。 <html> <ヘッド> <m...