Rumah  >  Artikel  >  hujung hadapan web  >  Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

青灯夜游
青灯夜游ke hadapan
2022-11-17 20:56:271972semak imbas

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

虚拟dom dan diffAlgoritma ialah titik sukar dalam vue proses pembelajaran dan titik pengetahuan yang mesti dikuasai semasa temu duga. Kedua-duanya saling melengkapi dan merupakan teras kepada rangka kerja vue. Hari ini kita akan meringkaskan algoritma vue2 dan 虚拟dom dalam diff. (Belajar perkongsian video: tutorial video vue)

Apakah itu VNode

Kami tahu bahawa render function akan ditukar menjadi VNode. VNode sebenarnya adalah pokok berdasarkan objek JavaScript, menggunakan atribut objek untuk menerangkan nod Malah, ia hanyalah lapisan abstraksi DOM sebenar. Akhirnya, pokok ini boleh dipetakan ke persekitaran sebenar melalui beberapa siri operasi.

Contohnya, jika ia adalah seperti berikut: template

<template>
  <span class="demo" v-show="isShow"> This is a span. </span> 
</template>

dan gantikannya dengan VNode, ia akan kelihatan seperti ini pada masa hadapan

{
  tag: "span",
  data: {
    /* 指令集合数组 */
    directives: [
      {
        /* v-show指令 */
        rawName: "v-show",
        expression: "isShow",
        name: "show",
        value: true,
      },
    ],
    /* 静态class */
    staticClass: "demo",
  },
  text: undefined,
  children: [
    /* 子节点是一个文本VNode节点 */
    {
      tag: undefined,
      data: undefined,
      text: "This is a span.",
      children: undefined,
    },
  ],
};

Secara amnya, VNode Hanya objek JavaScript. Objek JavaScript ini mewakili sepenuhnya 真实DOM.

Mengapa vue menggunakan VNode

Penulis berpendapat ada dua sebab

  • Sebab Virtual DOM jadi JavaScript Ia berdasarkan objek dan tidak bergantung pada persekitaran platform sebenar, jadi ia mempunyai keupayaan merentas platform, seperti platform penyemak imbas, Weex, Node, dsb.

  • Kurangkan operasi DOM Untuk sebarang perubahan halaman, hanya gunakan VNode untuk perbandingan operasi Hanya kemas kini pemasangan terakhir diperlukan DOM, mengelakkan operasi yang kerap , mengurangkan aliran semula pelayar dan lukis semula untuk DOMmeningkatkan prestasi halaman.

algoritma diff

Mari kita lihat

yang terlibat dalam kemas kini komponen. diff算法

Seperti yang dinyatakan sebelum ini apabila kita bercakap tentang pengumpulan kebergantungan, kaedah

yang diluluskan oleh 渲染watcher kepada Watcher sebenarnya ialah kaedah get. updateComponent

updateComponent = () => {
  vm._update(vm._render(), hydrating)
}

new Watcher(vm, updateComponent, noop, {
  before () {
    if (vm._isMounted) {
      callHook(vm, &#39;beforeUpdate&#39;)
    }
  }
}, true /* isRenderWatcher */)
Jadi komponen akan mencetuskan kaedah ini semula apabila data responsif berubah Seterusnya, mari analisa kaedah

dalam updateComponent secara terperinci. _update

_update

membuat perbezaan antara pemaparan awal dan kemas kini dalam kaedah

Walaupun kedua-dua kaedah dipanggil, parameter yang diluluskan adalah sama. _update__patch__

Mari kita lihat kaedah
// src/core/instance/lifecycle.js

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
  const vm: Component = this
  const prevEl = vm.$el
  const prevVnode = vm._vnode
  vm._vnode = vnode
  // 初次渲染没有 prevVnode,组件更新才会有
  if (!prevVnode) {
    // 初次渲染
    vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
  } else {
    // 更新
    vm.$el = vm.__patch__(prevVnode, vnode)
  }
  
  // ...
}

__patch__

__patch__

kaedah menerima empat parameter ,

ialah patch dan ialah oldVnode, jadi pemaparan awal tidak mempunyai vm.$el. nulloldVnode

// src/core/vdom/patch.js

return function patch (oldVnode, vnode, hydrating, removeOnly) {
  // 新节点不存在,只有oldVnode就直接销毁,然后返回
  if (isUndef(vnode)) {
    if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
    return
  }

  let isInitialPatch = false
  const insertedVnodeQueue = []
  // 没有老节点,直接创建,也就是初始渲染
  if (isUndef(oldVnode)) {
    isInitialPatch = true
    createElm(vnode, insertedVnodeQueue)
  } else {
    const isRealElement = isDef(oldVnode.nodeType)
    // 不是真实dom,并且是相同节点走patch
    if (!isRealElement && sameVnode(oldVnode, vnode)) {
      // 这里才会涉及到diff算法
      patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
    } else {
      if (isRealElement) {
        // ...
      }

      // replacing existing element
      const oldElm = oldVnode.elm
      const parentElm = nodeOps.parentNode(oldElm)

      // 1.创建一个新节点
      createElm(
        vnode,
        insertedVnodeQueue,
        // extremely rare edge case: do not insert if old element is in a
        // leaving transition. Only happens when combining transition +
        // keep-alive + HOCs. (#4590)
        oldElm._leaveCb ? null : parentElm,
        nodeOps.nextSibling(oldElm)
      )

      // 2.更新父节点占位符
      if (isDef(vnode.parent)) {
        let ancestor = vnode.parent
        const patchable = isPatchable(vnode)
        while (ancestor) {
          for (let i = 0; i < cbs.destroy.length; ++i) {
            cbs.destroy[i](ancestor)
          }
          ancestor.elm = vnode.elm
          if (patchable) {
            for (let i = 0; i < cbs.create.length; ++i) {
              cbs.create[i](emptyNode, ancestor)
            }
            
            const insert = ancestor.data.hook.insert
            if (insert.merged) {
              // start at index 1 to avoid re-invoking component mounted hook
              for (let i = 1; i < insert.fns.length; i++) {
                insert.fns[i]()
              }
            }
          } else {
            registerRef(ancestor)
          }
          ancestor = ancestor.parent
        }
      }

      // 3.删除老节点
      if (isDef(parentElm)) {
        removeVnodes([oldVnode], 0, 0)
      } else if (isDef(oldVnode.tag)) {
        invokeDestroyHook(oldVnode)
      }
    }
  }
   
   //触发插入钩子
  invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
  return vnode.elm
}
Proses anggaran kaedah adalah seperti berikut:

patch

    Tiada nod baharu, hanya nod lama dan nod lama dipadamkan secara langsung.
  • Hanya nod baharu dan tiada nod lama ditambahkan secara langsung.
  • Jika terdapat kedua-dua nod baharu dan nod lama, ia akan dinilai sama ada nod tersebut adalah sama, masukkan
  • .

    Kami akan fokus pada analisis nanti. pathVnodepatchVnode

  • Jika terdapat kedua-dua nod baharu dan nod lama, ia akan dinilai sama ada ia adalah nod yang sama Jika ia tidak sama, nod lama akan dipadamkan terus dan nod baharu akan ditambah.
  • Mari kita lihat cara ia menentukan bahawa ia adalah nod yang sama.

Untuk menentukan sama ada dua
// src/core/vdom/patch.js

function sameVnode (a, b) {
  return (
    a.key === b.key &&
    a.asyncFactory === b.asyncFactory && (
      (
        a.tag === b.tag &&
        a.isComment === b.isComment &&
        isDef(a.data) === isDef(b.data) &&
        sameInputType(a, b)
      ) || (
        isTrue(a.isAsyncPlaceholder) &&
        isUndef(b.asyncFactory.error)
      )
    )
  )
}

function sameInputType (a, b) {
  if (a.tag !== &#39;input&#39;) return true
  let i
  const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type
  const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type
  return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)
}
nod adalah nod yang sama, syarat berikut perlu dipenuhi serentak

VNode

  • sama

    key

  • kedua-duanya mempunyai fungsi kilang komponen tak segerak
  • (nama label nod semasa) adalah sama

    tag

  • Sama ada Adakah kedua-dua nod ulasan

    isComment

  • (objek yang sepadan dengan nod semasa mengandungi beberapa maklumat data khusus dan merupakan jenis VNodeData)

    data

  • Apabila teg ialah
  • ,

    mestilah sama <input>type

  • Apabila
dua

s adalah sama dan VNode ditakrifkan atau tidak ditakrifkan pada masa yang sama , dan jika teg itu tag、key、isComment mestilah sama. Pada masa ini, kedua-dua data ini dikira sebagai input则type dan anda boleh terus melakukan operasi VNode. sameVnodepatchVnode

patchVnode

Mari analisa kaedah

secara terperinci di bawah.

patchVnode

// src/core/vdom/patch.js

function patchVnode (
  oldVnode,
  vnode,
  insertedVnodeQueue,
  ownerArray,
  index,
  removeOnly
) {
  // 两个vnode相同则直接返回
  if (oldVnode === vnode) {
    return
  }

  if (isDef(vnode.elm) && isDef(ownerArray)) {
    // clone reused vnode
    vnode = ownerArray[index] = cloneVNode(vnode)
  }

  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
  }

  /*
    如果新旧VNode都是静态的,同时它们的key相同(代表同一节点),
    并且新的VNode是clone或者是标记了once(标记v-once属性,只渲染一次),
    那么只需要替换componentInstance即可。
  */
  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
  /*调用prepatch钩子*/
  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)) {
    /*新老节点均有children子节点,则对子节点进行diff操作,调用updateChildren*/
    if (isDef(oldCh) && isDef(ch)) {
      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
    /*如果只有新节点有子节点,先清空elm文本内容,然后为当前DOM节点加入子节点。*/
    } else if (isDef(ch)) {
      if (process.env.NODE_ENV !== &#39;production&#39;) {
        checkDuplicateKeys(ch)
      }
      if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, &#39;&#39;)
      addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
    /*如果只有老节点有子节点,则移除elm所有子节点*/
    } else if (isDef(oldCh)) {
      removeVnodes(oldCh, 0, oldCh.length - 1)
    /*当新老节点都无子节点的时候,因为这个逻辑中新节点text不存在,所以直接去除ele的文本*/
    } else if (isDef(oldVnode.text)) {
      nodeOps.setTextContent(elm, &#39;&#39;)
    }
  // 新节点是文本节点,如果文本不一样就设置新的文本  
  } else if (oldVnode.text !== vnode.text) {
    nodeOps.setTextContent(elm, vnode.text)
  }
  /*调用postpatch钩子*/
  if (isDef(data)) {
    if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)
  }
}
Proses anggaran kaedah adalah seperti berikut:

patchVnode1 Jika nod lama dan baru adalah sama, kembalikan terus.

2.如果新旧VNode都是静态的,同时它们的key相同(代表同一节点),并且新的VNode是clone或者是标记了once(标记v-once属性,只渲染一次),那么只需要替换componentInstance即可。

3.新节点不是文本节点,新老节点均有children子节点,则对子节点进行diff操作,调用updateChildren,这个updateChildrendiff算法的核心,后面我们会重点说。

4.新节点不是文本节点,如果老节点没有子节点而新节点存在子节点,先清空老节点DOM的文本内容,然后为当前DOM节点加入子节点。

5.新节点不是文本节点,当新节点没有子节点而老节点有子节点的时候,则移除该DOM节点的所有子节点。

6.新节点不是文本节点,并且新老节点都无子节点的时候,只需要将老节点文本清空。

7.新节点是文本节点,并且新老节点文本不一样,则进行文本的替换。

updateChildren(diff算法核心)

updateChildrendiff算法的核心,下面我们来重点分析。

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

这两张图代表旧的VNode与新VNode进行patch的过程,他们只是在同层级的VNode之间进行比较得到变化(相同颜色的方块代表互相进行比较的VNode节点),然后修改变化的视图,所以十分高效。所以Diff算法是:深度优先算法。 时间复杂度:O(n)

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

  const canMove = !removeOnly

  if (process.env.NODE_ENV !== &#39;production&#39;) {
    checkDuplicateKeys(newCh)
  }

  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    if (isUndef(oldStartVnode)) {
      oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
    } else if (isUndef(oldEndVnode)) {
      oldEndVnode = oldCh[--oldEndIdx]
    // 老 VNode 节点的头部与新 VNode 节点的头部是相同的 VNode 节点,直接进行 patchVnode,同时 oldStartIdx 与 newStartIdx 向后移动一位。
    } else if (sameVnode(oldStartVnode, newStartVnode)) {
      patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
      oldStartVnode = oldCh[++oldStartIdx]
      newStartVnode = newCh[++newStartIdx]
    // 两个 VNode 的结尾是相同的 VNode,同样进行 patchVnode 操作。并将 oldEndVnode 与 newEndVnode 向前移动一位。
    } else if (sameVnode(oldEndVnode, newEndVnode)) {
      patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
      oldEndVnode = oldCh[--oldEndIdx]
      newEndVnode = newCh[--newEndIdx]
    // oldStartVnode 与 newEndVnode 符合 sameVnode 的时候,
    // 将 oldStartVnode.elm 这个节点直接移动到 oldEndVnode.elm 这个节点的后面即可。
    // 然后 oldStartIdx 向后移动一位,newEndIdx 向前移动一位。
    } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
      patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
      canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
      oldStartVnode = oldCh[++oldStartIdx]
      newEndVnode = newCh[--newEndIdx]
    // oldEndVnode 与 newStartVnode 符合 sameVnode 时,
    // 将 oldEndVnode.elm 插入到 oldStartVnode.elm 前面。
    // oldEndIdx 向前移动一位,newStartIdx 向后移动一位。
    } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
      patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
      canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
      oldEndVnode = oldCh[--oldEndIdx]
      newStartVnode = newCh[++newStartIdx]
    } else {
      // createKeyToOldIdx 的作用是产生 key 与 index 索引对应的一个 map 表
      if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
      idxInOld = isDef(newStartVnode.key)
        ? oldKeyToIdx[newStartVnode.key]
        : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
      // 如果没有找到相同的节点,则通过 createElm 创建一个新节点,并将 newStartIdx 向后移动一位。
      if (isUndef(idxInOld)) { // New element
        createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
      } else {
        vnodeToMove = oldCh[idxInOld]
        // 如果找到了节点,同时它符合 sameVnode,则将这两个节点进行 patchVnode,将该位置的老节点赋值 undefined
        // 同时将 newStartVnode.elm 插入到 oldStartVnode.elm 的前面
        if (sameVnode(vnodeToMove, newStartVnode)) {
          patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
          oldCh[idxInOld] = undefined
          canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
        } else {
          // 如果不符合 sameVnode,只能创建一个新节点插入到 parentElm 的子节点中,newStartIdx 往后移动一位。
          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
        }
      }
      newStartVnode = newCh[++newStartIdx]
    }
  }
  // 当 while 循环结束以后,如果 oldStartIdx > oldEndIdx,说明老节点比对完了,但是新节点还有多的,
  // 需要将新节点插入到真实 DOM 中去,调用 addVnodes 将这些节点插入即可。
  if (oldStartIdx > oldEndIdx) {
    refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
    addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
  // 如果满足 newStartIdx > newEndIdx 条件,说明新节点比对完了,老节点还有多,
  // 将这些无用的老节点通过 removeVnodes 批量删除即可。
  } else if (newStartIdx > newEndIdx) {
    removeVnodes(oldCh, oldStartIdx, oldEndIdx)
  }
}

vue2diff算法采用的是双端比较,所谓双端比较就是新列表旧列表两个列表的头与尾互相对比,在对比的过程中指针会逐渐向内靠拢,直到某一个列表的节点全部遍历过,对比停止。

首尾对比的四种情况

我们首先来看看首尾对比的四种情况。

  • 使用旧列表的头一个节点oldStartNode新列表的头一个节点newStartNode对比

  • 使用旧列表的最后一个节点oldEndNode新列表的最后一个节点newEndNode对比

  • 使用旧列表的头一个节点oldStartNode新列表的最后一个节点newEndNode对比

  • 使用旧列表的最后一个节点oldEndNode新列表的头一个节点newStartNode对比

首先是 oldStartVnode 与 newStartVnode 符合 sameVnode 时,说明老 VNode 节点的头部与新 VNode 节点的头部是相同的 VNode 节点,直接进行 patchVnode,同时 oldStartIdx 与 newStartIdx 向后移动一位。

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

其次是 oldEndVnode 与 newEndVnode 符合 sameVnode,也就是两个 VNode 的结尾是相同的 VNode,同样进行 patchVnode 操作并将 oldEndVnode 与 newEndVnode 向前移动一位。

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

接下来是两种交叉的情况。

先是 oldStartVnode 与 newEndVnode 符合 sameVnode 的时候,也就是老 VNode 节点的头部与新 VNode 节点的尾部是同一节点的时候,将 oldStartVnode.elm 这个节点直接移动到 oldEndVnode.elm 这个节点的后面即可。然后 oldStartIdx 向后移动一位,newEndIdx 向前移动一位。

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

同理,oldEndVnode 与 newStartVnode 符合 sameVnode 时,也就是老 VNode 节点的尾部与新 VNode 节点的头部是同一节点的时候,将 oldEndVnode.elm 插入到 oldStartVnode.elm 前面。同样的,oldEndIdx 向前移动一位,newStartIdx 向后移动一位。

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

Akhir sekali, apabila tiada satu pun situasi di atas dipenuhi, bagaimana untuk menangani situasi ini?

Cari dan bandingkan

Itulah cari dan bandingkan.

mula-mula menjana jadual createKeyToOldIdx sepadan dengan indeks oldVnode dan key index melalui kaedah map.

Kemudian berdasarkan newStartVnode.key, kita boleh dengan cepat mendapatkan indeks oldKeyToIdx nod dengan createKeyToOldIdx yang sama daripada key (nilai pulangan idxInOld), dan kemudian mencari yang sama nod.

Terdapat tiga situasi di sini

  • Jika nod yang sama tidak ditemui, buat nod baharu melalui createElm dan gerakkan newStartIdx ke belakang satu Bit.

  • Jika nod ditemui dan ia mematuhi sameVnode, maka kedua-dua nod adalah patchVnode dan nod lama pada kedudukan itu diberikan nilai undefined (jika ada masih Jika nod baharu mempunyai kunci yang sama seperti nod, ia boleh dikesan dan digesa bahawa terdapat kunci pendua), dan newStartVnode.elm dimasukkan di hadapan oldStartVnode.elm. Dengan cara yang sama, newStartIdx bergerak ke belakang satu kedudukan.

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

  • Jika ia tidak sepadan dengan sameVnode, anda hanya boleh mencipta nod baharu dan memasukkannya ke dalam nod anak daripada parentElm. newStartIdx Berundur satu kedudukan.

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

Tambah dan padam nod

Langkah terakhir adalah mudah, apabila while gelung tamat Pada masa hadapan, jika oldStartIdx > oldEndIdx, ini bermakna nod lama telah dibandingkan, tetapi masih terdapat banyak nod baharu perlu dimasukkan ke dalam DOM sebenar. Hanya panggil addVnodes untuk memasukkan nod ini.

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

Begitu juga, jika syarat newStartIdx > newEndIdx dipenuhi, bermakna perbandingan nod baru telah selesai dan masih terdapat banyak nod lama yang tidak berguna ini dipadamkan secara berkelompok melalui removeVnodes Itu sahaja.

Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2

Ringkasan

Algoritma diff ialah algoritma perbandingan . Bandingkan dua 旧虚拟DOM和新虚拟DOM, bandingkan 虚拟节点 yang mana telah berubah, cari ini 虚拟节点 dan hanya kemas kini 真实节点 yang sepadan dengan nod maya ini, tanpa mengemas kini nod lain yang datanya tidak berubah, untuk mencapai kemas kini 精准 DOM sebenar, dan kemudian 提高效率和性能.

精准 terutamanya ditunjukkan dalam fakta bahawa algoritma diff mula-mula mencari nod boleh guna semula , dan kemudian mengalihkannya ke kedudukan yang betul . Apabila elemen tidak ditemui, buat nod baharu.

Sambungan

Mengapa anda perlu menggunakan kunci dalam vue dan apakah peranannya?

key ialah satu-satunya tanda Vue dalam vnode Melalui key ini, operasi diff boleh menjadi lebih tepat dan lebih pantas.

  1. adalah lebih tepat: kerana dengan key tiada penggunaan semula di tempat dan penggunaan semula di tempat boleh dielakkan dalam perbandingan sameNode fungsi a.key === b.key. Jadi ia akan menjadi lebih tepat.
  2. lebih pantas: Gunakan keunikan key untuk menjana map objek untuk mendapatkan nod yang sepadan, yang lebih pantas daripada kaedah traversal.

Mengapa tidak disyorkan untuk menggunakan indeks sebagai kunci

Gunakannya apabila senarai kami hanya melibatkan paparan, bukan pengisihan, pemadaman atau penambahanindex Tidak salah untuk menjadi key. Kerana index pada masa ini adalah unik pada setiap elemen.

Tetapi apabila ia melibatkan pengisihan, pemadaman dan penambahan, anda tidak boleh lagi menggunakan index sebagai key, kerana setiap elemen key tidak lagi unik. key bukan unik dan tidak membantu algoritma diff Menulisnya sama seperti tidak menulisnya.

(Belajar perkongsian video: pembangunan bahagian hadapan web, Video pengaturcaraan asas)

Atas ialah kandungan terperinci Pemahaman mendalam tentang algoritma VNode dan diff dalam vue2. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam