什么是VNode
在此之前我们先来聊一下什么是虚拟DOM~
它实际上是对真实DOM的抽象,是以js对象(VNode)作为基础的树
- 虚拟DOM
1、虚拟dom就是通过一个对象描述一个html结构
2、在js对象和真实dom树之间存在的一个虚拟对
3、所有的dom树节点都是根据这个虚拟dom实现生成的
4、在虚拟dom向真实的dom树转换之前会根据diff算法动态的计算需要更改的标签 进行替换操作
- 为什么需要虚拟DOM
当我们操作DOM元素的时候,消耗还是挺大的;比如一个操作需要更新多个元素的时候,浏览器要进行多次的重新渲染。
虚拟DOM的做法是:多次更新dom请求时,不会立即操作真实DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,避免大量的无谓计算。
虚拟DOM的优势:
1、通过diff算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗
2、抽象了原本的渲染过程,实现了跨平台的能力
- VNode
vue中可以通过createElement方法生成VNode
export function createElement (
context: Component,
tag: any,
data: any,
children: any,
normalizationType: any,
alwaysNormalize: boolean
): VNode | Array<VNode> {
}
diff算法
diff 算法是一种通过同层的树节点进行比较的高效算法
其有两个特点:
1、比较只会在同层级进行, 不会跨层级比较
2、在diff比较的过程中,循环从两边向中间比较
关键源码分析
function patch(oldVnode, vnode, hydrating, removeOnly) {
if (isUndef(vnode)) { // 没有新节点,直接执行destory钩子函数
if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
return
}
let isInitialPatch = false
const insertedVnodeQueue = []
if (isUndef(oldVnode)) {
isInitialPatch = true
createElm(vnode, insertedVnodeQueue) // 没有旧节点,直接用新节点生成dom元素
} else {
const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElement && sameVnode(oldVnode, vnode)) {
// 判断旧节点和新节点自身一样,一致执行patchVnode
patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
} else {
// 否则直接销毁及旧节点,根据新节点生成dom元素
if (isRealElement) {
if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
oldVnode.removeAttribute(SSR_ATTR)
hydrating = true
}
if (isTrue(hydrating)) {
if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
invokeInsertHook(vnode, insertedVnodeQueue, true)
return oldVnode
}
}
oldVnode = emptyNodeAt(oldVnode)
}
return vnode.elm
}
}
}