只保留关键代码,里面有伪代码
// 判断两个节点是否值得比较
function sameVnode(a, b) {
return (
a.key === b.key && // key值
a.tag === b.tag && // 标签名
a.isComment === b.isComment && // 是否为注释节点
// 是否都定义了data,data包含一些具体信息,例如onclick , style
isDef(a.data) === isDef(b.data) && sameInputType(a, b) // 当标签是的时候,type必须相同
)
}
//新旧节点整体比较,若不值得继续diff,直接用vnode 替换 oldVnode
function patch(oldVnode, vnode) {
…
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode)
} else {
// 当前oldVnode对应的真实元素节点 const oEl = oldVnode.el
// 父元素
let parentEle = api.parentNode(oEl)
// 根据Vnode生成新元素节点
createEle(vnode)
if (parentEle !== null) {
// 将新元素添加进父元素
api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl))
// 移除以前的旧元素节点
api.removeChild(parentEle,oldVnode.el)
oldVnode = null
}
}
// some code
return vnode
}
//对两个节点进行细节比较,主要就是children 和 text
function patchVnode(oldVnode, vnode) {
//找到对应的真实 dom
const el = vnode.el = oldVnode.el
let i, oldCh = oldVnode.children, ch = vnode.children
if (oldVnode === vnode) return
//都有文本节点并且不相等,那么将el的文本节点设置为Vnode的文本节点。
if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
api.setTextContent(el, vnode.text)
} else {
updateEle(el, vnode, oldVnode)
//都有子节点,则执行updateChildren函数比较子节点
if (oldCh && ch && oldCh !== ch) {
updateChildren(el, oldCh, ch)
} else if (ch) {
// 如果oldVnode没有子节点而Vnode有,则将Vnode的子节点真实化之后添加到el
createEle(vnode)
} else if (oldCh) {
//oldVnode有子节点而Vnode没有,则删除el的子节点
api.removeChildren(el)
}
}
}
function updateChildren(parentElm, oldCh, newCh) {
// oldVnode.children的开始指针
let oldStartIdx = 0,
//vnode.children的开始指针
let newStartIdx = 0,
//oldVnode.children的尾指针
let oldEndIdx = oldCh.length - 1
// oldVnode.children里的第一个节点
let oldStartVnode = oldCh[0]
//oldVnode.children里的最后一个节点
let oldEndVnode = oldCh[oldEndIdx]
//vnode.children的尾指针
let newEndIdx = newCh.length - 1
//vnode.children里的第一个节点
let newStartVnode = newCh[0]
// vnode.children里的最后一个节点
let newEndVnode = newCh[newEndIdx]
//{key:index} oldVnode.Children 生成的集合
let oldKeyToIdx
//vnode.children的子元素在
oldVnode的集合
let idxInOld
//需要移动的子节点
let elmToMove
//oldVnode.children先遍历完时, vnode.children对应的尾指针的el
let before
//指针对应的vnode为null时,左边的指针+1 右边的指针-1
oldVnode.children最左侧的节点正好和vnode.children最右侧的节点匹配,oldStartIdx右移1位(-1),newEndIdx左移1位(+1)
oldVnode.children最右侧的节点正好和vnode.children最左侧的节点匹配,oldEndIdx右移1位(-1),newStartIdx左移1位(+1)
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (oldStartVnode == null) {
oldStartVnode = oldCh[++oldStartIdx]
} else if (oldEndVnode == null) {
oldEndVnode = oldCh[–oldEndIdx]
} else if (newStartVnode == null) {
newStartVnode = newCh[++newStartIdx]
} else if (newEndVnode == null) {
newEndVnode = newCh[–newEndIdx]
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode)
oldStartVnode = oldCh[++oldStartIdx]
newStartVnode = newCh[++newStartIdx]
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode)
oldEndVnode = oldCh[–oldEndIdx]
newEndVnode = newCh[–newEndIdx]
} else if (sameVnode(oldStartVnode, newEndVnode)) {
//复用节点 移动节点
patchVnode(oldStartVnode, newEndVnode)
api.insertBefore(parentElm, oldStartVnode.el, api.nextSibling(oldEndVnode.el))
oldStartVnode = oldCh[++oldStartIdx]
newEndVnode = newCh[–newEndIdx]
} else if (sameVnode(oldEndVnode, newStartVnode)) {
patchVnode(oldEndVnode, newStartVnode)
api.insertBefore(parentElm, oldEndVnode.el, oldStartVnode.el)
oldEndVnode = oldCh[–oldEndIdx]
newStartVnode = newCh[++newStartIdx]
} else {
if (oldKeyToIdx === undefined) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) // 有key生成index表
}
idxInOld = oldKeyToIdx[newStartVnode.key]
//新节点的key 在旧节点中不存在,直接创建新节点,影响性能消耗
if (!idxInOld) {
api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el)
newStartVnode = newCh[++newStartIdx]
} else {
//存在的话就是复用旧节点,所以 v-for必须设置key 而且必须是唯一的
elmToMove = oldCh[idxInOld]
//key的值不到万不得已不要用数组的下标index,当操作改变dom顺序时,也不会复用节点,而是创建新节点
if (elmToMove.sel !== newStartVnode.sel) {
api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el)
} else {
patchVnode(elmToMove, newStartVnode)
oldCh[idxInOld] = null
api.insertBefore(parentElm, elmToMove.el, oldStartVnode.el)
}
newStartVnode = newCh[++newStartIdx]
}
}
}
//如果旧节点先遍历完,把剩余的新节点按索引插入真实dom
if (oldStartIdx > oldEndIdx) {
before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].el
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx)
} else if (newStartIdx > newEndIdx) {
//新节点先遍历完,把旧节点中多余的节点移除
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
}
}
312

被折叠的 条评论
为什么被折叠?



