1.diff算法是什么?

diff算法是一种通过**同层的树节点**进行比较的高效算法

Diff 算法探讨的就是虚拟 DOM 树发生变化后,生成 DOM 树更新补丁的方式。对比新旧两株虚拟 DOM 树的变更差异,将更新补丁作用于真实 DOM,以最小成本完成视图更新。

1.1特点

策略:深度优先,同层比较

1.2原理分析

updateChildren

updateChildren使用同级比对,同级比对图示

使用首尾指针法:

0. 依次对比,比较成功后退出当前比较
1. 渲染结构以newvnode为准
2. 每次比较成功后,start和end向中间靠拢
3. 当新旧节点中有一个start跑到end的右侧时,终止比较
4. 如果都匹配不到,则旧的虚拟dom key只去对比新的虚拟dom key值,如果key相同,则复用


好,了解了这些规则,让我们利用一个案例看看 ,源码会如何执行吧。

简单贴一下源码吧,以下是更新子节点 diff核心算法,有一些我自己写的注释和问题

 function updateChildren(
parentElm,
oldCh,
newCh,
insertedVnodeQueue,
removeOnly
) {
//节点的序号
let oldStartIdx = 0
let oldEndIdx = oldCh.length - 1 let newStartIdx = 0
let newEndIdx = newCh.length - 1
//节点的value
let oldStartVnode = oldCh[0]
let oldEndVnode = oldCh[oldEndIdx] let newStartVnode = newCh[0]
let newEndVnode = newCh[newEndIdx] let oldKeyToIdx, idxInOld, vnodeToMove, refElm // removeOnly is a special flag used only by <transition-group>
// to ensure removed elements stay in correct relative positions
// during leaving transitions
const canMove = !removeOnly if (__DEV__) {
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]
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(
oldStartVnode,
newStartVnode,
insertedVnodeQueue,
newCh,
newStartIdx
)
oldStartVnode = oldCh[++oldStartIdx]
newStartVnode = newCh[++newStartIdx]
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(
oldEndVnode,
newEndVnode,
insertedVnodeQueue,
newCh,
newEndIdx
)
oldEndVnode = oldCh[--oldEndIdx]
newEndVnode = newCh[--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]
} 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 {
//todo oldKeyToIdx这是啥?
if (isUndef(oldKeyToIdx))
//这是取得什么?{key:index}
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
//isDef:不是undefind or null
/***
* 如果有key 获取key对应的index,
* 如果没有key,则
*/
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
if (isUndef(idxInOld)) {//如果 oldCh里面没有此节点
// New element
createElm(
newStartVnode,
insertedVnodeQueue,
parentElm,
oldStartVnode.elm,
false,
newCh,
newStartIdx
)
} else {//如果 oldCh有此节点
//那就拿到值
vnodeToMove = oldCh[idxInOld]
if (sameVnode(vnodeToMove, newStartVnode)) {
patchVnode(
vnodeToMove,
newStartVnode,
insertedVnodeQueue,
newCh,
newStartIdx
)
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(oldCh, oldStartIdx, oldEndIdx)
}
} function checkDuplicateKeys(children) {
const seenKeys = {}
for (let i = 0; i < children.length; i++) {
const vnode = children[i]
const key = vnode.key
if (isDef(key)) {
if (seenKeys[key]) {
warn(
`Duplicate keys detected: '${key}'. This may cause an update error.`,
vnode.context
)
} else {
seenKeys[key] = true
}
}
}
} //从oldCh找到与node相同的值,返回old对应的i
function findIdxInOld(node, oldCh, start, end) {
for (let i = start; i < end; i++) {
const c = oldCh[i]
if (isDef(c) && sameVnode(node, c)) return i
}
} function patchVnode(
oldVnode,
vnode,
insertedVnodeQueue,
ownerArray,
index,
removeOnly?: any
) {
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
} // reuse element for static trees.
// note we only do this if the vnode is cloned -
// if the new node is not cloned it means the render functions have been
// reset by the hot-reload-api and we need to do a proper re-render.
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 (__DEV__) {
checkDuplicateKeys(ch)
}
if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
} else if (isDef(oldCh)) {
removeVnodes(oldCh, 0, oldCh.length - 1)
} else if (isDef(oldVnode.text)) {
nodeOps.setTextContent(elm, '')
}
} 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)
}
}

探究vue的diff算法的更多相关文章

  1. Vue 中 diff 算法后更新 DOM 的方法

    vue 2.0加入了 virtual dom,在 node_modules\vue\src\core\vdom\patch.js 中会对虚拟 DOM 进行 diff 算法等,然后更新 DOM. 网上的 ...

  2. Vue中diff算法的理解

    Vue中diff算法的理解 diff算法用来计算出Virtual DOM中改变的部分,然后针对该部分进行DOM操作,而不用重新渲染整个页面,渲染整个DOM结构的过程中开销是很大的,需要浏览器对DOM结 ...

  3. 详解vue的diff算法

    前言 我的目标是写一个非常详细的关于diff的干货,所以本文有点长.也会用到大量的图片以及代码举例,目的让看这篇文章的朋友一定弄明白diff的边边角角. 先来了解几个点... 1. 当数据发生变化时, ...

  4. vue的diff算法

    前言 我的目标是写一个非常详细的关于diff的干货,所以本文有点长.也会用到大量的图片以及代码举例,目的让看这篇文章的朋友一定弄明白diff的边边角角. 先来了解几个点... 1. 当数据发生变化时, ...

  5. 详解vue的diff算法原理

    我的目标是写一个非常详细的关于diff的干货,所以本文有点长.也会用到大量的图片以及代码举例,目的让看这篇文章的朋友一定弄明白diff的边边角角. 先来了解几个点... 1. 当数据发生变化时,vue ...

  6. diff算法深入一下?

    文章转自豆皮范儿-diff算法深入一下 一.前言 有同学问:能否详细说一下 diff 算法. 简单说:diff 算法是一种优化手段,将前后两个模块进行差异化比较,修补(更新)差异的过程叫做 patch ...

  7. Vue源码终笔-VNode更新与diff算法初探

    写完这个就差不多了,准备干新项目了. 确实挺不擅长写东西,感觉都是罗列代码写点注释的感觉,这篇就简单阐述一下数据变动时DOM是如何更新的,主要讲解下其中的diff算法. 先来个正常的html模板: & ...

  8. vue diff 算法学习

    function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartIdx ...

  9. 理解Vue 2.5的Diff算法

    DOM"天生就慢",所以前端各大框架都提供了对DOM操作进行优化的办法,Angular中的是脏值检查,React首先提出了Virtual Dom,Vue2.0也加入了Virtual ...

  10. 图解vue中 v-for 的 :key 的作用,虚拟dom Diff算法

    其实不只是vue,react中在执行列表渲染时也会要求给每个组件添加上key这个属性. 要解释key的作用,不得不先介绍一下虚拟DOM的Diff算法了. 我们知道,vue和react都实现了一套虚拟D ...

随机推荐

  1. C#希尔排序算法

    前言 希尔排序简单的来说就是一种改进的插入排序算法,它通过将待排序的元素分成若干个子序列,然后对每个子序列进行插入排序,最终逐步缩小子序列的间隔,直到整个序列变得有序.希尔排序的主要思想是通过插入排序 ...

  2. 【RocketMQ】顺序消息实现总结

    全局有序 在RocketMQ中,如果使消息全局有序,可以为Topic设置一个消息队列,使用一个生产者单线程发送数据,消费者端也使用单线程进行消费,从而保证消息的全局有序,但是这种方式效率低,一般不使用 ...

  3. Solution -「ZJOI 2014」力

    Descrption Link. 对于每一个 \(i\),求出: \[\sum_{j=1}^{i-1}\frac{a_{j}}{(i-j)^{2}}-\sum_{j=i+1}^{n}\frac{a_{ ...

  4. slate源码解析(三)- 定位

    接口定义 能够对于文字.段落乃至任何元素的精准定位 并做出增删改查,都是在开发一款富文本编辑器时一项最基本也是最重要的功能之一.让我们先来看看Slate中对于如何在文档树中定位元素是怎么定义的[源码] ...

  5. C#学习笔记--变量类型的转换

    变量类型的转化: 转换原则 同类型的大的可以装小的,小类型的装大的就需要强制转换. 隐式转换: 同种类型的转换: //有符号 long-->int-->short-->sbyte l ...

  6. Arduino Uno 引脚 –

    Arduino Uno 引脚 – Arduino Uno 板有 20 多个引脚,可用于许多不同的应用.在这篇文章中,我将为您提供 Arduino Uno 主要引脚的完整实用概述. 如果您刚开始使用 A ...

  7. 【Unity3D】消融特效

    1 前言 ​ 选中物体消融特效中基于 Shader 实现了消融特效,本文将基于 Shader Graph 实现消融特效,两者原理一样,只是表达方式不同,另外,选中物体消融特效中通过 discard 丢 ...

  8. 什么???CSS也能原子化!

    1.什么是原子化 CSS? Atomic CSS is the approach to CSS architecture that favors small, single-purpose class ...

  9. 【XXE漏洞】原理及实践演示

    一.原理 XML是用于传输和存储数据的一种格式,相当于一种信息传输工具,其中包含了XML声明,DTD文档类型定义.文档元素. XXE是xml外部实体注入漏洞,发生在应用程序解析XML输入时,没有禁止外 ...

  10. db-cdc之mysql 深入了解并使用binlog

    1.什么是binlog? 2.binlog可以用来干什么? 3.怎么样使用binlog? binlog是记录所有数据库表结构变更(例如CREATE.ALTER TABLE-)以及表数据修改(INSER ...