什么是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.elm = elm
this.ns = undefined
this.context = this.context
this.functionalContext = undefined
this.functionalScopeId = undefined
this.functionalOptions = 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
}
}
从上面代码可以看到,vnode是从VNode实例化的一个普通的JavaScript对象,用这个对象来描述一个真实DOM元素的话,那么该DOM元素上所有的属性在VNode对象上存在对应的属性。
简单的说,vnode可以理解成节点描述对象,她描述了应该怎样去创建真实的DOM节点。
vnode创建DOM并插入到视图
VNode的作用
由于每次渲染视图都是先创建vnode,然后使用它创建真实DOM插入到页面中,所以可以将上一次渲染视图时所创建的vnode缓存起来,之后每当需要重新渲染视图时,将新创建的vnode和上一次缓存的vnode对比,查看它们之间的不同地方,找出这些不同点再去修改真实DOM。
vue目前对状态的侦测策略采用了中等粒度。
变化侦测通知到组件级别
如图,当某个状态发生改变时,只通知到组件级别,然后组件内使用虚拟DOM来渲染视图,也就是说组件中众多状态中有一个发生变化,那么整个组件就要重新渲染,很明显这样会造成很大的性能浪费。所以,vnode进行缓存并和当前新建的vnode进行对比,只更新变化的节点就显得尤为重要。
VNode类型
vnode的类型有以下几种:
1、 注释节点
2、 文本节点
3、 元素节点
4、 组件节点
5、 函数式组件
6、 克隆节点
当使用VNode类创建一个vnode时,通过参数为实例设置属性时,无效的属性会默认被赋值为undefined或者false,所以下面只讨论vnode有哪些有效属性。
注释节点:
由于创建注释节点过程简单,直接代码说明它的属性:
export const createEmptyVNode = text => {
const node = new VNode()
node.text = text
node.isComment = true
return node
}
可以看到,一个注释节点只有两个有效属性:text和isComment,其余默认undefined或者false
例如,一个真实的注释节点:
<!-- 注释节点 -->
所对应的vnode如下:
text: '注释节点',
isComment: true
文本节点:
当文本类型被创建时,有个一个text属性:
{
text: 'hello, world'
}
克隆节点:
克隆节点是将现有节点的属性复制到新的节点中,让新创建的节点和被克隆节点属性保持一致,它的作用是优化静态节点和插槽节点。
以静态节点为例,当组件的某个状态发生变化后 ,当前组件会通过虚拟DOM重新渲染视图,静态节点因为它的内容不会改变,所以除了首次渲染需要执行渲染函数获取vnode外,后续更新不需要执行渲染函数重新生成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
cloned.isCloned = true
if (deep && vnode.children) {
cloned.children = cloneVNode(vnode.children)
}
}
可以看到,克隆现有节点时,只需要将现有节点属性复制到新的节点即可。克隆节点和被克隆节点之间唯一区别是isCloned属性,克隆节点的isCloned为true,被克隆的原始节点为false
元素节点:
元素节点通常有以下4个有效属性:
tag:节点名称
data: 该属性包含了一些节点上的数据,比如attrs, class和style等
children: 字节点列表
context: 当前组件vue实例
例如,一个真实元素节点:
<p><span>hello</span><span>world</span></p>
{
children: [VNode, VNode],
context: {...},
data: {...},
tag: 'p',
...
}
组件节点:
组件节点和元素节点类似,但有两个独有属性:
componentOptions: 组件节点的选项参数,其中包含propsData、tag和children等信息。
componentInstance: 组件实例
函数式组件:
有两个独有属性:
functionalContext和functionalOptions
通常一个函数组件vnode如下:
{
componentInstance: {...},
componentOptions: {...},
context: {...},
data: {...},
tag: 'div'
}
总结
VNode是一个类,可以生成不容类型的vnode实例,对应不同类型的真实DOM元素。
由于vue采用了虚拟DOM更新视图,当属性变化时,整个组件都要进行重新渲染的操作,但组件内并不是所有DOM节点都要更新,所以将vnode缓存并将新生成的vnode进行对比,只更新变化部分以提升性能。
网友评论