对于VNode的一些理解

239 阅读2分钟

什么是VNode

在此之前我们先来聊一下什么是虚拟DOM~

它实际上是对真实DOM的抽象,是以js对象(VNode)作为基础的树

  • 虚拟DOM

1、虚拟dom就是通过一个对象描述一个html结构

2、在js对象和真实dom树之间存在的一个虚拟对

3、所有的dom树节点都是根据这个虚拟dom实现生成的

4、在虚拟dom向真实的dom树转换之前会根据diff算法动态的计算需要更改的标签 进行替换操作

  • 为什么需要虚拟DOM

企业微信截图_bc0cdad3-d0de-4ba3-9968-bfb0841ef215.png

当我们操作DOM元素的时候,消耗还是挺大的;比如一个操作需要更新多个元素的时候,浏览器要进行多次的重新渲染。

虚拟DOM的做法是:多次更新dom请求时,不会立即操作真实DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,避免大量的无谓计算。

虚拟DOM的优势:

1、通过diff算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗

2、抽象了原本的渲染过程,实现了跨平台的能力

  • VNode

企业微信截图_b9ae1e59-9ba5-4231-9f87-caf85dc4108c.png

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

}

}

}