目录
1 什么是VNode
在vue中存在一个VNode类,使用它可以实例化不同类型的vnode实例,而不同类型的vnode实例各自表示不同类型的dom元素。
例如:dom元素有元素节点、文本节点和注释节点等,vnode实例也会对着有元素节点、文本节点和注释节点等。
export default class VNode {
constructor(
tag,
data,
children,
text,
elm,
context,
componentOptions,
asyncFactory
) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.functionalContent = undefined
this.functionalOptions = undefined
this.functionalScopeId = undefined
this.key = data & data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
get child() {
return this.componentInstance
}
}
本质上其实是js其中一个普通的对象,是从VNode类实例化的对象。我们用这个js对象来描述一个真实dom元素的话,那么该dom元素上的所有属性在vnode这个对象上都存在对应的属性。简单的说,vnode可以理解成节点描述对象。
vnode表示一个真实的dom元素,所有真实的dom节点都使用vnode创建并插入到页面中,如下图所示:
2 VNode的作用
由于每次渲染视图时都是先创建vnode,然后使用它创建真实dom插入到页面中,所以可以将上一次渲染视图时所创建的vnode缓存起来,之后每当需要重新渲染视图时,将新创建的vnode和上一次缓存的vnode进行对比,查看它们之间有哪些不一样的地方,找出这些不一样的地方并基于此去修改真实的dom。
3 VNode的类型
vnode的类型有以下几种:
1.注释节点;
2.文本节点;
3.元素节点;
4.组件节点;
5.函数式组件;
6.克隆节点;
不同类型的vnode之间其实只是属性不同,准备的说是有效属性不同,无效属性一般赋值为undefined或false。
3.1 注释节点
export const createEmptyVNode = (text) => {
const node = new VNode()
node.text = text
node.isComment = true
return node
}
注释节点其实就两个有效属性:text、isComment;
例如:<!-- 注释节点 -->
对应的vnode:
{
text: '注释节点',
isComment: true,
}
3.2 文本节点
export function createTextVNode(val) {
return new VNode(undefined, undefined, undefined, String(val))
}
文本节点一个有效属性;
{
text: "Hello World!"
}
3.3 克隆节点
克隆节点是将现有节点的属性复制到新节点中,让新创建的节点和被克隆节点的属性保持一致,从而实现克隆效果。它的作用是优化静态节点和插槽节点(slot node)。
静态节点为例,当组件内的某个状态发生变化后,当前组件会通知虚拟dom重新渲染视图,静态节点因为它的内容不会变化,所以除了首次渲染需要执行渲染函数获取vnode之外,后续更新不需要执行渲染函数重新生成vnode。这是就会使用克隆节点的方法将vnode克隆一份,使用克隆节点进行渲染。
export function cloneVNode(vnode, deep) {
const cloned = new VNode(
vnode.tag,
vnode.data,
vnode.children,
vnode.text,
vnode.elm,
vnode.context,
vnode.componentOptions,
vnode.asyncFactory
)
cloned.ns = vnode.ns
cloned.isStatic = vnode.isStatic
cloned.key = vnode.key
cloned.isComment = vnode.isComment
clone.isCloned = true
if (deep && vnode.children) {
clone.children = cloneVNode(vnode.children)
}
return cloned
}
克隆节点和被克隆的节点之间唯一的区别是isCloned属性,克隆节点的isCloned为true,被克隆的原始节点的isCloned为false。
3.4 元素节点
元素节点板通常会存在以下4种有效属性:
tag:节点的名称;
data:节点上的数据;
children:当前节点的子节点列表;
context:当前组件的vue实例;
案例:
<p>
<span>hello</span>
<span>Berwin</span>
</p>
{
children: [VNode, VNode],
context: {
//...
},
data: {
// ...
},
tag: 'p',
// ...
}
3.5 组件节点
组件节点和元素节点类似,有以下两个独有的属性:
1.componentOptions:组件节点的选项参数,其中包含propsData、tag、children等;
2.componentInstance:组件实例;
案例:
组件:<child></child>
{
componentInstance: {},
componentOptions: {},
context: {},
data: {},
tag: 'vue-component-1-child',
// ...
}
3.6 函数式组件
函数式组件和组件节点类似,它有两个特有的属性funcctionalContext和cuncional-Options;
{
functionalContext: {},
functionalOptions: {},
context: {},
data: {},
tag: 'div',
}
4 总结
VNode是一个类,可以生成不同类型的vnode实例,而不同类型的vnode表示不同类型的真实dom元素。
vnode有很多类型,本质上都是从VNode类实例化出的对象,其唯一区别只是属性不同。
注:本文章来自于《深入浅出vue.js》(人民邮电出版社)阅读后的笔记整理