ホームページ > ウェブフロントエンド > jsチュートリアル > vue の差分アルゴリズムの知識ポイントのまとめ

vue の差分アルゴリズムの知識ポイントのまとめ

亚连
リリース: 2018-05-28 11:26:33
オリジナル
1142 人が閲覧しました

この記事では、Vue の差分アルゴリズムに関する関連知識の概要を共有します。興味のある方は参照してください。

仮想dom

diffアルゴリズムは、まずdiffのオブジェクトが仮想domであり、実際のdomの更新はdiffアルゴリズムの結果であるという概念を明確にする必要があります

Vnode基本クラス

 constructor (
  。。。
 ) {
  this.tag = tag
  this.data = data
  this.children = children
  this.text = text
  this.elm = elm
  this.ns = undefined
  this.context = context
  this.fnContext = undefined
  this.fnOptions = undefined
  this.fnScopeId = undefined
  this.key = data && data.key
  this.componentOptions = componentOptions
  this.componentInstance = undefined
  this.parent = undefined
  this.raw = false
  this.isStatic = false
  this.isRootInsert = true
  this.isComment = false
  this.isCloned = false
  this.isOnce = false
  this.asyncFactory = asyncFactory
  this.asyncMeta = undefined
  this.isAsyncPlaceholder = false
 }
ログイン後にコピー

のこの部分コードは主に更新用です。 diff アルゴリズムの特定の差分属性の意味を知ることは良いことです。 もちろん、vnode インスタンスについてもよく理解できます

プロセス全体

コア機能は patch 関数です。

  • isUndef判定(未定義かnullか)

  • //空のマウント(コンポーネントとして)、新しいルート要素を作成createElm(vnode,insertVnodeQueue) ここで、作成したノードが1つも挿入されていないことがわかります。 1 つですが、統合バッチ処理のためにキューに入れられます

  • コア関数 SameVnode

  • function sameVnode (a, b) {
     return (
      a.key === b.key && (
       (
        a.tag === b.tag &&
        a.isComment === b.isComment &&
        isDef(a.data) === isDef(b.data) &&
        sameInputType(a, b)
       ) || (
        isTrue(a.isAsyncPlaceholder) &&
        a.asyncFactory === b.asyncFactory &&
        isUndef(b.asyncFactory.error)
       )
      )
     )
    }
    ログイン後にコピー
これは、2 つのノードのキー、タグ (ラベル)、およびデータを直接比較する外部比較関数です (データはここでは VNodeData を指します)、入力タイプを直接比較します。

export interface VNodeData {
 key?: string | number;
 slot?: string;
 scopedSlots?: { [key: string]: ScopedSlot };
 ref?: string;
 tag?: string;
 staticClass?: string;
 class?: any;
 staticStyle?: { [key: string]: any };
 style?: object[] | object;
 props?: { [key: string]: any };
 attrs?: { [key: string]: any };
 domProps?: { [key: string]: any };
 hook?: { [key: string]: Function };
 on?: { [key: string]: Function | Function[] };
 nativeOn?: { [key: string]: Function | Function[] };
 transition?: object;
 show?: boolean;
 inlineTemplate?: {
  render: Function;
  staticRenderFns: Function[];
 };
 directives?: VNodeDirective[];
 keepAlive?: boolean;
}
ログイン後にコピー

これにより、2 つのノードにさらなる比較値があるかどうかが確認され、そうでない場合は直接置換されます

置換プロセスは主に createElm 関数であり、もう 1 つは oldVNode を破棄することです

// destroy old node
    if (isDef(parentElm)) {
     removeVnodes(parentElm, [oldVnode], 0, 0)
    } else if (isDef(oldVnode.tag)) {
     invokeDestroyHook(oldVnode)
    }
ログイン後にコピー

簡略化して挿入プロセスノードのタイプを決定し、それを個別に呼び出すことです

createComponent (子があるかどうかを決定し、それを再帰的に呼び出します)

createComment

createTextNode

作成後に挿入関数を使用します

その後、次のことを行う必要がありますハイドレート関数を使用して、仮想 dom と実際の dom をマッピングします

function insert (parent, elm, ref) {
  if (isDef(parent)) {
   if (isDef(ref)) {
    if (ref.parentNode === parent) {
     nodeOps.insertBefore(parent, elm, ref)
    }
   } else {
    nodeOps.appendChild(parent, elm)
   }
  }
 }
ログイン後にコピー

core 関数

 function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
  if (oldVnode === vnode) {
   return
  }

  const elm = vnode.elm = oldVnode.elm

  if (isTrue(oldVnode.isAsyncPlaceholder)) {
   if (isDef(vnode.asyncFactory.resolved)) {
    hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
   } else {
    vnode.isAsyncPlaceholder = true
   }
   return
  }

  if (isTrue(vnode.isStatic) &&
   isTrue(oldVnode.isStatic) &&
   vnode.key === oldVnode.key &&
   (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
  ) {
   vnode.componentInstance = oldVnode.componentInstance
   return
  }

  let i
  const data = vnode.data
  if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
   i(oldVnode, vnode)
  }

  const oldCh = oldVnode.children
  const ch = vnode.children
  if (isDef(data) && isPatchable(vnode)) {
   for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
   if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
  }
  if (isUndef(vnode.text)) {
   if (isDef(oldCh) && isDef(ch)) {
    if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
   } else if (isDef(ch)) {
    if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, &#39;&#39;)
    addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
   } else if (isDef(oldCh)) {
    removeVnodes(elm, oldCh, 0, oldCh.length - 1)
   } else if (isDef(oldVnode.text)) {
    nodeOps.setTextContent(elm, &#39;&#39;)
   }
  } else if (oldVnode.text !== vnode.text) {
   nodeOps.setTextContent(elm, vnode.text)
  }
  if (isDef(data)) {
   if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)
  }
 }
ログイン後にコピー

const el = vnode.el = oldVnode.el これは非常に重要なステップです。vnode.el に現在の実際の dom を参照させます。 el が変更されると、vnode.el も同期的に変更されます。

  1. 2つの参照が一致しているか比較してください

  2. その後asyncFactoryが何をするのかが分からないので、理解するのが難しいです

  3. 静的ノードはキーが同じであれば比較しません。再レンダリングされ、componentInstance が直接コピーされます (ここでは、once コマンドが有効になります)

  4. vnode がテキスト ノードまたはアノテーション ノードであるが、vnode.text != oldVnode.text の場合は、テキストを更新するだけで済みますvnode.elm の内容

  5. 子の比較

  • oldVnode のみに子ノードがある場合は、これらのノードを削除します

  • vnode のみに子ノードがある場合は、これらの子ノードを作成します。 textノードには、vnode.elmのテキストを設定します

  • の場合は、updateChildrenが更新されます

  • oldVnodeもvnodeも子ノードを持たず、oldVnodeがテキストノードの場合。またはコメント ノードで、vnode.elm のテキストを空の文字列に設定します

updateChildren

この部分の焦点は依然としてアルゴリズム全体にあります

最初の 4 つのポインター、oldStart、oldEnd、newStart、newEnd、 2 つの配列、oldVnode、Vnode。

function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
  let oldStartIdx = 0
  let newStartIdx = 0
  let oldEndIdx = oldCh.length - 1
  let oldStartVnode = oldCh[0]
  let oldEndVnode = oldCh[oldEndIdx]
  let newEndIdx = newCh.length - 1
  let newStartVnode = newCh[0]
  let newEndVnode = newCh[newEndIdx]
  let oldKeyToIdx, idxInOld, vnodeToMove, refElm

  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
   if (isUndef(oldStartVnode)) {
    oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
   } else if (isUndef(oldEndVnode)) {
    oldEndVnode = oldCh[--oldEndIdx]
   } else if (sameVnode(oldStartVnode, newStartVnode)) {
    patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
    oldStartVnode = oldCh[++oldStartIdx]
    newStartVnode = newCh[++newStartIdx]
   } else if (sameVnode(oldEndVnode, newEndVnode)) {
    patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
    oldEndVnode = oldCh[--oldEndIdx]
    newEndVnode = newCh[--newEndIdx]
   } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
    patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
    canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
    oldStartVnode = oldCh[++oldStartIdx]
    newEndVnode = newCh[--newEndIdx]
   } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
    patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
    canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
    oldEndVnode = oldCh[--oldEndIdx]
    newStartVnode = newCh[++newStartIdx]
   } else {
    if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
    idxInOld = isDef(newStartVnode.key)
     ? oldKeyToIdx[newStartVnode.key]
     : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
    if (isUndef(idxInOld)) { // New element
     createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
    } else {
     vnodeToMove = oldCh[idxInOld]
     if (sameVnode(vnodeToMove, newStartVnode)) {
      patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
      oldCh[idxInOld] = undefined
      canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
     } else {
      // same key but different element. treat as new element
      createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
     }
    }
    newStartVnode = newCh[++newStartIdx]
   }
  }
  if (oldStartIdx > oldEndIdx) {
   refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
   addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
  } else if (newStartIdx > newEndIdx) {
   removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
  }
 }
ログイン後にコピー

ループ比較のいくつかの状況と処理 (以下の ++ -- すべてインデックスの ++ を参照します --) 比較は比較対象のノードです。この比較では、sameVnode 関数が使用されます。これは当てはまりません

ループ全体が終了しないための条件 oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx

  1. oldStart === newStart, oldStart++ newStart++

  2. oldEnd === newEnd 、 oldEnd-- newEnd--

  3. oldStart === newEnd、oldStart はキューの最後に挿入されます oldStart++ newEnd--

  4. oldEnd === newStart、oldEnd はキューの先頭に挿入されます oldEnd- - newStart++

  5. 残りの状況はすべてこのように処理されます。簡単に言うと、処理後、newStart++

  • newStartがoldで同じものを見つけて、oldStartの前に移動します。

  • 同じものが見つからない場合は、作成してoldStartの前に置きます
  • ループが終了しても完了ではありません

完了するまでにはまだ判定期間があります

if (oldStartIdx > oldEndIdx) {
   refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
   addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
  } else if (newStartIdx > newEndIdx) {
   removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
  }
ログイン後にコピー

簡単に言うと、ループが終了したら、古い配列と新しい配列の 4 つのポインターの間の内容を確認して、より多くを返し、より少ないものを補うだけです

上記は私が皆さんに役立つようにまとめたものです。将来。

関連記事:

コンポーネント自体のラベルにスタイルクラスを追加するAngular5の方法

クロスドメインリクエストを実装するVue-cli開発環境の方法

Vue-cli webpackモバイルターミナルの詳細説明自動ビルドレム問題

以上がvue の差分アルゴリズムの知識ポイントのまとめの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート