背景
之所以要用diff算法,是因为渲染一棵真实的dom树的开销很大,比如修改某个结点,重排或者重绘dom树会消耗大量的时间,所以Vue采用diff算法来解决这个问题。
diff算法的本质是找出两个dom对象之间的差异,目的是尽可能的复用结点。这里的dom对象是vue中的virtual dom也就是虚拟dom,也就是说用js对象来表示页面中的dom结构。
这是真实的dom
<div id="app">
<p id="app-child">
this is a p
</p>
</div>
复制代码
一棵dom树主要包括三个部分:
- 自身的标签名。div
- 自身的属性名 id=”app-child”
- 子节点 p
所以我们可以设计如下的对象结构表示:(伪代码)
这是虚拟的dom:
let Vnode={
tag:'div',
children:[
{
tag:'p',
innerText:'123'
}
]
}
复制代码
Vnode也是对象
如何比较?
在次啊用dom算法比较新旧结点的时候,比较只会在同一层级进行比较,也就是和兄弟,堂兄弟,不会对父子结点进行比较。
diff算法流程图
具体分析
path打补丁
function patch(oldVnode,vnode){
if(sameVnode(oldVnode,vnode)){
patchVnode(oldVnode,vnode)
}else{
const oe=oldVnode.e1 //当前oldVnode对应的真实父元素结点
let parentElement=api.parentNode(oe) //父元素
createEle(vnode) //根据Vnode生成新元素
if(parentElement!=null){
api.insertBefore(parentElement,vnode.e1,api.nextSibling(oe)) //将新元素添加进父元素中
api.removeChild(parentElement,oldVnode.e1) //移除以前的就元素结点
oldVnode=null
}
}
}
复制代码
patch函数接收两个参数oldVnode和Vnode分别代表新的结点和旧的结点
判断两个结点是否值得比较,值得比较则执行patchVnode
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必须相同
)
}
复制代码
如果子节点一样,那么就深入检查他们的子结点。因为diff是逐层比较,如果第一层不一样,就不会比较第二层了。(这样设计虽然存在一定的子节点一样的情况会导致不能重复利用,但是这样果断抛弃的可以减少错误)
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)
}
}
}
复制代码
- el为虚拟dom
- 判断Vnode和oldVnode是否指向同一个对象,如果是,直接return
- 如果他们都有text结点并且不相等,那么将el的文本结点设置为Vnode的文本结点。
- 如果oldVnode有子节点而Vnode没有,则将Vnode的子节点实例化之后添加到el
- 如果oldCh存在children结点把那个且oldCh不等于ch则执行updateChildren函数,else 如果chbu不为null,执行createEle()函数,添加vnode进去。如果oldCh部位null,则移除el
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END