diff算法使只更新我们修改的那一小块dom而不要更新整个dom:
在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较:
判断是否是相同节点:
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) // 当标签是<input>的时候,type必须相同 ) }
如果两个节点是一样的,那么就深入检查他们的子节点。如果两个节点不一样那就可以直接替换oldVnode
:
function patch (oldVnode, vnode) { // some code if (sameVnode(oldVnode, vnode)) { patchVnode(oldVnode, vnode) // 相同节点打补丁 } else { const oEl = oldVnode.el // 当前oldVnode对应的真实元素节点 let parentEle = api.parentNode(oEl) // 父元素 createEle(vnode) // 根据Vnode生成新元素 if (parentEle !== null) { api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 将新元素添加进父元素 api.removeChild(parentEle, oldVnode.el) // 移除以前的旧元素节点 oldVnode = null } } // some code return vnode }
当我们确定两个节点相同之后我们会对两个节点执行patchVnode
方法:
patchVnode (oldVnode, vnode) { const el = vnode.el = oldVnode.el let i, oldCh = oldVnode.children, ch = vnode.children if (oldVnode === vnode) return if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) { api.setTextContent(el, vnode.text) }else { updateEle(el, vnode, oldVnode) if (oldCh && ch && oldCh !== ch) { updateChildren(el, oldCh, ch) }else if (ch){ createEle(vnode) //create el's children dom }else if (oldCh){ api.removeChildren(el) } } }
这个函数做了以下事情:
- 找到对应的真实dom,称为
el
- 判断
Vnode
和oldVnode
是否指向同一个对象,如果是,那么直接return
- 如果他们都有文本节点并且不相等,那么将
el
的文本节点设置为Vnode
的文本节点。 - 如果
oldVnode
有子节点而Vnode
没有,则删除el
的子节点 - 如果
oldVnode
没有子节点而Vnode
有,则将Vnode
的子节点真实化之后添加到el
- 如果两者都有子节点,则执行
updateChildren
函数比较子节点,这一步很重要
updateChildren
不设key,newCh和oldCh只会进行头尾两端的相互比较,设key后,除了头尾两端的比较外,还会从用key生成的对象oldKeyToIdx
中查找匹配的节点,所以为节点设置key可以更高效的利用dom。
先进行4种比较:
如果是oldS和E相同,那么真实dom中的第一个节点会移到最后;
如果是oldE和S相同,那么真实dom中的最后一个节点会移到最前;
如果oldS和S相同,会调用patchVnode方法,继续判断这两个节点的子节点,oldStartIndex,newStartIndex指向下个节点;
如果oldE和E相同,会调用patchVnode方法,继续判断这两个节点的子节点,oldStartIndex,newStartIndex指向上个节点;
如果以上都不匹配,就尝试在oldChildren中寻找跟newStartVnode具有相同key的节点,如果找不到相同key的节点,说明newStartVnode是一个新节点,就创建一个,然后把newStartVnode设置为下一个节点
我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点
如果以上都不行会依次比较如下:
即把C更新成F,D更新成C,E更新成D,最后再插入E
原文:https://www.cnblogs.com/wind-lanyan/p/9061684.html