网站首页 > 技术文章 正文
网上看了一些diff的算法,但是感觉看完之后,还是那么的一知半解,为什么一个简单的diff算法,不能直接画个流程图就简单的明了了呢,说动就动,下面的是本人基于vue版本2.6.11源码为各位读友进行的解析
Vue的diff流程图
流程前说明
- 由于diff的过程是对vnode(虚拟dom)树进行层级比较,所以以同一层级作为例子
- 下面将旧节点列表的起始和终止节点称为OS(OldStarVnode)和OE(OldEndVnode),用index标志遍历过程OS和OE的变化。即OS和OE的index称为OSIndex和OEIndex。同理得新节点的为NS和NE,NSIndex和NEIndex,如下图
主流程
如下图:
文字版描述一下就是:
- 判断是否遍历完,未遍历则开始2,否则,如果遍历完了旧节点列表,则未遍历的新节点则创建并且增加到节点列表,如果遍历完了新节点列表,则未遍历的旧节点在节点列表里面删除
- 对旧节点的OS和OE进行判空,如果为空,则跳过该节点,继续从1开始;否则继续3
- 对OS,OE,NS,NE进行两两比较,如果相等,则更新节点并且指针向下一个移动,继续从1开始;否则继续4
- 判断NS是否有key,有key则判断NS是否在旧节点列表里面找到key一样的进行更新;否则创建NS并且插入节点列表
updateChildren进行diff算法源码
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
// 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 (process.env.NODE_ENV !== 'production') {
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 {
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, 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)
}
}
附,源码中部分工具函数的解释:
isUndef 对节点进行判空
function isUndef (v) {
return v === undefined || v === null
}
sameVnode对节点进行判断是否相等
- 判断新旧节点的key
- 判断新旧节点的属性(tag,isComment表示是否是注释节点,isDef表示是否为非空节点,sameInputType表示是否同个Input节点)是否一致
- 判断新旧节点的加载函数asyncFactory是否一致
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)
)
)
)
}
patchVnode更新节点
patchVnode更新节点主要做以下事情,代码比较长就不贴了,影响读者,需要可以直接阅读源码:
- 判断vnode和oldvnode是否相等,相等直接返回
- 处理静态节点的情况
- 对vnode如果是可patch的情形进行调用update
- 对vnode进行判断是否是根节点(即文本节点),如果是,则进行5,否则则对其子节点进行遍历更新
- 判断vnode和oldvnode文本是否一样: 不一样则替换节点文本
猜你喜欢
- 2025-06-09 平面几何算法:求点到直线和圆的最近点
- 2025-06-09 解决雪花算法生成的ID传到前端后精度丢失问题
- 2025-06-09 什么是非对称加密算法?(什么是非对称加密,有哪些特点)
- 2025-06-09 React18的diff算法(react-diff-view)
- 2025-06-09 「算法题」判断一颗二叉树是否对称
- 2025-06-09 经典监督式学习算法 - 决策树(决策监督制度)
- 2025-06-09 「西瓜哥说算法」从前序与中序遍历序列构造二叉树
- 2024-09-29 前端算法面试题 前端面试题csdn
- 2024-09-29 每天一道算法题——最长连续递增序列
- 2024-09-29 前端知识杂记(diff算法自解) diff算法 react
你 发表评论:
欢迎- 593℃几个Oracle空值处理函数 oracle处理null值的函数
- 586℃Oracle分析函数之Lag和Lead()使用
- 574℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 571℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 567℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 559℃【数据统计分析】详解Oracle分组函数之CUBE
- 546℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 540℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端懒加载 (49)
- 前端获取当前时间 (50)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)