Javascript

vue diff 算法

本文主要是介绍vue diff 算法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

diff 算法:找出虚拟dom(vnode)之间的差异,尽可能的复用节点。

当我们改变一个节点的时候,我们其实主要改了以下部分:

  1. 自身的属性(style 、class等等)
  2. 子节点

那么 diff 算法可以抽象为两部分:

  1. diff(找出差异)
  2. patch(打补丁更新dom)

举个栗子:

给定旧数组: [a,b,c,d],新数组: [e,f,g,h],找出新旧数组之间的差异。

我们约定以下名词 - 旧首(旧数组的第一个元素) - 旧尾(旧数组的最后一个元素) - 新首(新数组的第一个元素) - 新尾(新数组的最后一个元素)

一些工具函数:

sameVnode

用于判断节点是否应该复用,这里做了一些简化,实际的diff算法复杂些,这里只用tag 和 key 相同,我们就复用节点,执行patchVnode,即对节点进行修改。

function sameVnode(a, b) {
  return a.key === b.key && a.tag === b.tag
}

createKeyToOldIdx

建立key-index的索引,主要是替代遍历,提升性能

function createKeyToOldIdx(children, beginIdx, endIdx) {
  let i, key
  const map = {}
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key
    if (isDef(key)) map[key] = i
  }
  return map
}

旧首 和 新首 对比

if (sameVnode(oldStartVnode, newStartVnode)) { 
      patchVnode(oldStartVnode.elm, oldStartVnode, newStartVnode);
      oldStartVnode = oldCh[++oldStartIdx];
      newStartVnode = newCh[++newStartIdx];
    }

旧尾 和 新尾 对比

if (sameVnode(oldEndVnode, newEndVnode)) { //旧尾 和 新尾相同
      patchVnode(oldEndVnode.elm, oldEndVnode, newEndVnode);
      oldEndVnode = oldCh[--oldEndIdx];
      newEndVnode = newCh[--newEndIdx];
    }

旧首 和 新尾 对比

if (sameVnode(oldStartVnode, newEndVnode)) { 
	//旧首 和 新尾相同,将旧首移动到 最后面
      patchVnode(oldStartVnode.elm, oldStartVnode, newEndVnode);
      nodeOps.insertBefore(parentElm, oldStartVnode.elm, oldEndVnode.elm.nextSibling)
      oldStartVnode = oldCh[++oldStartIdx];
      newEndVnode = newCh[--newEndIdx];
    }

旧尾 和 新首 对比,将 旧尾 移动到 最前面

if (sameVnode(oldEndVnode, newStartVnode)) {
	//旧尾 和 新首相同 ,将 旧尾 移动到 最前面
      patchVnode(oldEndVnode.elm, oldEndVnode, newStartVnode);
      nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
      oldEndVnode = oldCh[--oldEndIdx];
      newStartVnode = newCh[++newStartIdx];
    }

首尾对比都不符合 sameVnode 的话,尝试用newCh的第一项在 oldCh 内寻找sameVnode,如果在 oldCh 不存在对应的 sameVnode ,则直接创建一个,存在的话则判断符合 sameVnode,则移动 oldCh 对应的节点,不符合 sameVnode ,创建新节点,最后 通过 oldStartIdx > oldEndIdx ,来判断 oldCh 和 newCh 哪一个先遍历完成
oldCh 先遍历完成,则证明 newCh 还有多余节点,需要新增这些节点;newCh 先遍历完成,则证明 oldCh 还有多余节点,需要删除这些节点。

总结:
1. diff 算法的核心是子节点数组对比,思路是通过首尾两端对比。
2. key的作用是:决定节点是否可以复用,建立key-index的索引,主要是替代遍历,提升性能。

这篇关于vue diff 算法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!