ホームページ  >  記事  >  ウェブフロントエンド  >  diffアルゴリズムの使用方法の詳細な説明

diffアルゴリズムの使用方法の詳細な説明

php中世界最好的语言
php中世界最好的语言オリジナル
2018-04-20 16:05:412331ブラウズ

今回は diff アルゴリズムの使用方法について詳しく説明します。 diff アルゴリズムを使用する際の 注意事項 は何ですか?実際の事例を見てみましょう。

z

仮想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
 }
この部分コードの主な目的は、差分アルゴリズム内の特定の差分の

属性の意味をより深く理解することです。もちろん、vnode インスタンス

プロセス全体をよりよく理解することもできます

中心となる関数は patch 関数です

  • isUndef判定(未定義かnullではないか)

  • // 空のマウント(コンポーネントと思われる)、新しいルート要素を作成createElm(vnode,insertVnodeQueue) ここで、ノードの作成が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 を参照させます。 dom が変更されると、vnode.el も同期して変更されます。

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

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

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

  4. vnode がテキスト ノードまたは

    annotation ノードである場合、vnode.text != oldVnode.text のみが必要です。 vnode.elm のテキストコンテンツを更新します

  5. 子の比較

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

  • vnode のみに子ノードがある場合は、ここでこれらの子ノードを作成しますoldVnode がテキスト ノードの場合、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

    oldStart === newStart, oldStart++ newStart++
  1. oldEnd === newEnd 、 oldEnd-- newEnd--
  2. oldStart === newEnd、oldStart はキューの最後に挿入されます oldStart++ newEnd--
  3. oldEnd === newStart、oldEnd はキューの先頭に挿入されます oldEnd- - newStart++
  4. 残りの状況はすべてこのように処理されます。 簡単に言うと、処理後に newStart++
  5. の 2 つのプロセスがあります。
  • 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)
  }

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

JS EventEmitter使用详解

json数组键值大小写怎么转换(附代码)

以上がdiffアルゴリズムの使用方法の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。