什么是vdom
vdom 是一种使用js对象来描述真实dom的技术,通过这种,我们能精确知道哪些真实的Dom改变了,从而尽量减少Dom操作的性能开销。
snabbdom.js
vdom是通过snabbdom.js库实现的,大概过程有以下三部
1.compile (把真实的dom编译成Vnode);
2.diff (利用diff算法,比较oldVnode和newVnode之间有什么变化);
3.patch(把这些变化通过打补丁的方式更新到真实的dom上去);
snabbdom的两个核心函数 h(),patch()
snabbdom - h 函数
h()函数主要是根据传进来的参数,返回一个 vnode(虚拟节点)对象
snabbdom - patch 函数
patch()函数的两种用法
1. patch(container,vnode) //将虚拟dom渲染成真实的dom
2.patch(vnode,newVnode) // 利用diff算法比较oldVnode和newVnode的差异
什么是diff算法
diff算法很早就存在,一开始diff算法是计算两个文本的差异。所以一定要明确的是,diff 算法并不是react或者vue原创,它们只是通过diff 算法比较两个vnode差异,并只针对该部分进行原生的dom操作,而并非重新渲染整个页面。
vdom 中是在patch(vnode,newVode)比较新旧函数时会用到diff
以下是patch()函数的核心代码分析
function patch (oldVnode, vnode) {
......
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
}
}
.......
return vnode
}
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)
}
}
}
这个函数做了以下事情:
1.找到对应真实的dom,称为el
2.判断vnode和oldVode是否完全相同,如果是,那么直接返回 return
3.如果他们都有文本节点且不相等,则更新el的文本节点
4.如果oldVode有子节点,而vnode没有,则删除el的子节点
5.如果oldVode没有子节点,而vnode有,则将vnode的子节点真实化之后添加到el
6.如过两者都有子节点,则执行updateChildren函数比较子节点
updateChildren 函数比较复杂,感兴趣的同学可以去了解下源码。
vue的整体流程
1.第一步:解析模板成render函数
2.第二步:响应式开始监听
3.第三步:首次渲染,显示页面,且绑定依赖
4.第四步:data属性变化,触发render