仮想DOMのVueのソース学習(F) - DOM-DiffのVueの中(上)

1.はじめに

前回の記事で説明しVNode、私たちが言った時に、VNode最大の目的は、前と後のデータで真の変化を生成することであるDOM仮想対応するDOMノード、その後、次の2つの新旧を比較することができますVNode違いは、違いが、その後更新がある場所を見つけるために、DOM最終的に到達するためには、ノードを最小動作し本当のDOMビューを更新する目的。2新旧のコントラストVNodeとプロセスの違いを見つけるが呼び出されるDOM-Diffプロセス。DOM-Diff全体の仮想アルゴリズムするとDOMコアのは、次の、我々はソースコードを開始、での綿密な表情プロセスは次のようです。VueDOM-Diff

2.パッチ

ではVue真ん中、DOM-Diffプロセスと呼ばれるpatchプロセス。パッチ、「パッチ」を意味古いを参照するVNodeパッチ、新しい結果としてパッチVNodeカザフスタンの、非常にイメージ。どんなにそれが呼ばれているもの、その本質は、新旧2つの比較するのではないVNodeのプロセスを。私たちは次のことを勉強patch古いいわゆる:プロセスは、このような考えを把握しなければならない場合VNode(であるoldVNode)に対応するデータ変更ビューの前に仮想であるDOMノードを、新たなVNodeデータの変更が新しいビューに対応する描画できるようにした後、仮想DOMノードを、我々は新しい生成する必要があるので、VNode古いと比較して、基礎をoldVNode新しい場合、VNode一部のノードで、古いoldVNodeではない上、古いoldVNodeへのアドオンで、新しい場合はVNode無ノード上で、古いoldVNodeそこに、古い中oldVNodeで除去;新しいにおけるいくつかのノードた場合VNode、古いoldVNodeそこに、新しいVNode主題は、古い更新oldVNode、とても新しく、そのVNode異なります。

たぶん、あなたはそれが問題で、我々ははっきり話している、あなたはそれをこのように解釈することができていない、周りの少しを感じる:あなたは、古いコンピュータは、文書の電子版になりましたがあると、上司はあなたに紙の文書の新しいシートを与えた。この時、およびこれらの文書の両方を使って、文書の新鮮な紙のバージョンは、所有者に送信された文書の新しい電子版を行うには、ドキュメントの紙版を優先するものと、内容のほとんどが同じであることを伝えます。この時点で、この作業のために、次の2つのソリューションを持っている必要があります:1つのオプションは、それが古い文書の内容はすべて削除するもの、であるのかどうかで、その後、単語単位に紙文書の新しいバージョンに対してノック、このプログラムは、手数料の脳はポイントで問題を解決することができます疲れているんです。別のプログラムは、新しいドキュメントやマニュアルがない旧内の特定の部分がある場合、違いは何文書の紙のバージョンで文書の新しい電子版の古い外観に比べ、ベンチマークとしてドキュメントの新しい紙のバージョンに基づいていますが、その後、文書の内部の古い部分でそれらを一緒に入れて、文書の特定の部分がなかったし、そこに新たな文書の古い場合は、古い文書でこれらの部分を削除し、古いのいくつかの部分と新規場合ドキュメントは、更新が必要であるかどうかを確認し、古い文書の最終更新見て、紙の文書、完璧なソリューションと同じの手に最終版に到達するために比較し、持っています。

上記の二つの方式を対比し、明らかにあなたVue賢いとして、2番目のオプションを選択します。対応するビューでレンダリングされた古い文書の電子版で、第2のプログラムはoldVNode、紙に対応する文書の新しいバージョンは、新しい観点でレンダリングされますVNode言葉では:新しいVNODEベース、古いoldVNodeの変換は新しいVNODEのようにそれを作る、これは物事を行うには、パッチプロセスです

それは真実ではない、のように、それは非常に複雑な気持ちのように聞こえる、と言った、我々はそれについて考える、全体がpatchより多くの3つのことを行うよりも何もありません。

  • ノードを作成します:新しいVNode古いはありながらoldVNode、古いでは、ないoldVNode創造。
  • ノードを削除するには:新しいVNodeいいえ古いものはoldVNode古いからそれを持ってoldVNode削除。
  • 更新ノード:新しいVNodeと古いoldVNodeの両方で、新しいに関してVNode被験者は、古い更新しますoldVNode

OK、ここに、あなたは正しいVuepatch理解半分のプロセス、次に、我々は、いずれかで見いずれかを分析するVue方法を行うために、すべての上記の3つのもののために。

ノードを作成します3。

我々が分析前回の記事では、VNodeクラスは、ノードの6種類を記述することができますが、実際には、ノードの3種類のみを作成することができるとに挿入DOM途中、彼らは以下のとおりです。要素ノード、テキストノード、コメントノード。だから、Vueあなたが作成したときにノードが新しいを決定しますVNode古いを持っているoldVNode。このノードではないの作成とに挿入するために、異なるメソッドを呼び出すために、ノードのどのタイプの一部であるDOMミドル。

実際には、それはノードの3種類を備えているので、それは、また、決定することは困難であることは時にソースを判断する方法で非常に明白です。

// 源码位置: /src/core/vdom/patch.js
function createElm (vnode, parentElm, refElm) {
    const data = vnode.data
    const children = vnode.children
    const tag = vnode.tag
    if (isDef(tag)) {
        vnode.elm = nodeOps.createElement(tag, vnode)   // 创建元素节点
        createChildren(vnode, children, insertedVnodeQueue) // 创建元素节点的子节点
        insert(parentElm, vnode.elm, refElm)       // 插入到DOM中
    } else if (isTrue(vnode.isComment)) {
      vnode.elm = nodeOps.createComment(vnode.text)  // 创建注释节点
      insert(parentElm, vnode.elm, refElm)           // 插入到DOM中
    } else {
      vnode.elm = nodeOps.createTextNode(vnode.text)  // 创建文本节点
      insert(parentElm, vnode.elm, refElm)           // 插入到DOM中
    }
  }

上記のコードから、我々は見ることができます:

  • 要素ノードは、単純に決定するかどうかを決定するVNodeノードが持っているかどうかtagのタブを。ある場合はtag要素ノードであると考えられているプロパティを、それから呼び出すcreateElement方法は要素ノードを作成し、通常はその後、再帰的に作成されたすべての子ノードを横断し、要素ノードの子ノードがあるでしょう、良い後に作成されたすべてのサブノードinsert最後に、現在の要素ノードの内部に現在の要素ノードDOM
  • コメントノード、単に裁判官かどうかを決定VNodeするisCommentプロパティがあるかどうかtrue、それがされている場合、するtrueコメントノードに比べて、その後、呼び出しcreateCommentコメントノードを作成する方法を、その後に挿入DOM真ん中。
  • ノードが要素でもコメントノードでもない場合は、テキストノードは、その後、呼び出しと考えられているcreateTextNodeテキストノードを作成する方法を、その後に挿入DOM真ん中。

コードがnodeOpsあるVue、すべてのノードは、例えば、パッケージを操作するクロスプラットフォームの互換性にnodeOps.createTextNode()するブラウザと同等でdocument.createTextNode()

これは次のように全体のフローチャートであり、ノードを作成する動作を終了します。

4. [ノードの削除

その後、新しいノードの一部とはならばVNode、古いではないoldVNode、古いからノードへのこれらの必要性そして、そこにoldVNode削除が。削除ノードは非常に簡単です、単に削除されたノードのことを親要素に呼び出すremoveChild方法をすることができます。ソースとして、次のとおりです。

function removeNode (el) {
    const parent = nodeOps.parentNode(el)  // 获取父节点
    if (isDef(parent)) {
      nodeOps.removeChild(parent, el)  // 调用父节点的removeChild方法
    }
  }

5.更新したノード

ノードおよび削除のノードが比較的単純であり、ノードを更新作成することは比較的複雑ですが、実際にはロジックが理解できるようになるだけでソートから、あまり複雑ではありません。

アップデートのノードが新しい内のノードのいくつかのときでVNode、古いoldVNodeそこでは、我々は慎重に比較する必要があり、別の場所を更新することがわかります。

我々はノードを更新する前に、まず小さな、静的ノードの概念を導入何ですか?私たちは例を見て:

<p>我是不会变化的文字</p>

任意の変数変数なしで、テキストのみが含まれ、このノード、手段以上のものに関係なく、データの、その後どのように変化し、限りその後、初めてのレンダリングのためのノード、および、後に、その上には決して変更、理由データの変更は、それとは何の関係も発生しないので、それは、任意の変数が含まれていません。私たちは、このノードが静的ノードと呼ばれる呼び出します。

OK、このコンセプトに、我々はノードを更新するために始めていました。我々は、ノードの判断に次の3つの場合を更新する必要があり、個別に対処する場合:

  1. 場合VNodeoldVNode静的のノードがあります

    私たちは、その静的ノードは、その後、治療せずに直接スキップしているにかかわらず、すべての変更の静的なノードのデータはそれとは何の関係もない、と言います。

  2. もしVNodeテキストノード

    場合はVNode、このノードで表現されたテキストノードは、プレーンテキストのみが含まれている、あなたは簡単に見ることができoldVNode置けば、それは、二つの異なるテキストを比較するために異なっているかどうか、それがテキストノードであるかどうか、もしそうであればoldVNodeとにテキストでVNode同じテキスト。場合oldVNodeではないテキストノード、そしていいえ、それが何であるかは重要で、ダイレクトコールのsetTextNodeテキストノードとのテキストコンテンツにそれを回す方法VNodeと同じ。

  3. 場合はVNode要素ノードであります

    場合はVNode要素ノードがあり、その後、次の2つの状況を細分化:

    • ノードが子を持ちます

      新しいノード内の子ノードが含まれている場合、古いノードは、子ノードが含まれている場合は、古いノードを見て時間が、子ノードが含まれ、それは更新の子を再帰的にコントラストノード必要があり、古いノードが含まれていない場合古いノードが子ノードに配置する空のノード新しいノードが古いノードがテキストである場合、ノードは、その後、古いの内部に挿入されて作成している場合、子ノードは、古いノードは、空のノードまたはテキストノードがあるかもしれませんノード、空のテキストを入れて、その後、古い内部に挿入されたノードの新しい子ノードのノードを作成します。

    • ノードが子ノードを持ちません

      それがテキストノードではありませんしながら、ノードは、子ノードが含まれていない場合、それは関係なく、古いノードの前に、直接空にすることができているものはありません、それは簡単にハンドルにある、ノードが空ノードであることを示しています。

OK、以上の3例を終え、ノード、その後、私たちは見ていても、更新の基本的な完成は、特定のソースを達成する方法で、以下のように、ソースコードは次のとおりです。

// 更新节点
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
  // vnode与oldVnode是否完全一样?若是,退出程序
  if (oldVnode === vnode) {
    return
  }
  const elm = vnode.elm = oldVnode.elm

  // vnode与oldVnode是否都是静态节点?若是,退出程序
  if (isTrue(vnode.isStatic) &&
    isTrue(oldVnode.isStatic) &&
    vnode.key === oldVnode.key &&
    (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
  ) {
    return
  }

  const oldCh = oldVnode.children
  const ch = vnode.children
  // vnode有text属性?若没有:
  if (isUndef(vnode.text)) {
    // vnode的子节点与oldVnode的子节点是否都存在?
    if (isDef(oldCh) && isDef(ch)) {
      // 若都存在,判断子节点是否相同,不同则更新子节点
      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
    }
    // 若只有vnode的子节点存在
    else if (isDef(ch)) {
      /**
       * 判断oldVnode是否有文本?
       * 若没有,则把vnode的子节点添加到真实DOM中
       * 若有,则清空Dom中的文本,再把vnode的子节点添加到真实DOM中
       */
      if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
      addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
    } 
    // 若只有oldnode的子节点存在
    else if (isDef(oldCh)) {
      // 清空DOM中的子节点
      removeVnodes(elm, oldCh, 0, oldCh.length - 1)
    } 
    // 若vnode和oldnode都没有子节点,但是oldnode中有文本
    else if (isDef(oldVnode.text)) {
      // 清空oldnode文本
      nodeOps.setTextContent(elm, '')
    }
    // 上面两个判断一句话概括就是,如果vnode中既没有text,也没有子节点,那么对应的oldnode中有什么就清空什么
  } 
  // 若有,vnode的text属性与oldVnode的text属性是否相同?
  else if (oldVnode.text !== vnode.text) {
    // 若相同:用vnode的text替换真实DOM的文本
    nodeOps.setTextContent(elm, vnode.text)
  }
}

上記のコードのコメントは非常に明確に書かれている、と我々は全体のプロセスのフロー・チャートを整理するフローチャートを描く次のとおりです。

フローチャートやコードを照射することによって、私は、論理ノード更新の一部は、あなたが簡単に理解できると信じています。

また、あなたは、古いものと新しいものならば、気づいたかもしれませんVNode含まれている子ノードでは、コード内で子ノードを更新するため呼び出すupdateChildren方法を、そして最後に、このアプローチの論理は、我々は次の記事で学ん拡大置くものです。

6.まとめ

この記事では、導入されたアルゴリズムを:パッチプロセスを。私たちはまず、アルゴリズムによってコーミング全体を理解するために、プロセス全体アルゴリズムの考え方を導入したノード、削除ノード、更新ノードを作成するには:プロセス、すなわち、3つのことをやりました。そして、すべてのコントロールソースは、論理フロー図を示す詳細な調査を開始しました。ノードを更新することに加えて、新旧の場合はノードが含まれている子で、私たちは次の記事で学び始めた子ノードの更新処理について詳細なサブノードを更新する必要があります。(終わり)VueDOM-DiffpatchVNode

おすすめ

転載: www.cnblogs.com/wangjiachen666/p/11846299.html