在上一篇博客中,我们介绍了core/instance
这个文件夹所做的事情,这篇博客,我们来介绍在Vue中非常重要的一个概念VNode
,在Vue源码中,VNode定义在core/vdom/vnode.js
这个文件中
我们都知道Vue使用了虚拟DOM的概念,而VNode则是非常重要的一环,它是对真实节点的一层封装,而不依赖于某个平台,可以是浏览器和WEEX,用属性描述真实DOM的每个特性,当它发生变化的时候,就会去修改对应的视图
VNode类
class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions
) {
/*当前节点的标签名*/
this.tag = tag
/*当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息*/
this.data = data
/*当前节点的子节点,是一个数组*/
this.children = children
/*当前节点的文本*/
this.text = text
/*当前虚拟节点对应的真实dom节点*/
this.elm = elm
/*当前节点的名字空间*/
this.ns = undefined
/*当前节点的编译作用域*/
this.context = context
/*函数化组件作用域*/
this.functionalContext = undefined
/*节点的key属性,被当作节点的标志,用以优化*/
this.key = data && data.key
/*组件的option选项*/
this.componentOptions = componentOptions
/*当前节点对应的组件的实例*/
this.componentInstance = undefined
/*当前节点的父节点*/
this.parent = undefined
/*简而言之就是是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false*/
this.raw = false
/*是否为静态节点*/
this.isStatic = false
/*是否作为跟节点插入*/
this.isRootInsert = true
/*是否为注释节点*/
this.isComment = false
/*是否为克隆节点*/
this.isCloned = false
/*是否有v-once指令*/
this.isOnce = false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}
上面就是VNode的类定义,每一个属性相关的解释见代码中,下面给出一个VNode实例的例子
const VNodeInstance = new VNode({
tag: 'div',
data: {
class: 'bupt',
},
children: [
{
tag: 'span',
data: {
class: 'mickey',
},
text: 'Hello, Vue',
}
],
});
这个例子渲染出来的真实DOM为
<div class="bupt">
<span class="mickey">
Hello, Vue
</span>
</div>
VNodeData
VNodeData是VNode中data属性的定义,下面给出一个VNodeData的例子
{
staticClass: 'bupt',
// 和`v-bind:class`一样的 API
// 接收一个字符串、对象或字符串和对象组成的数组
class: {
foo: true,
bar: false
},
staticStyle: {
border: '1px solid #000'
},
// 和`v-bind:style`一样的 API
// 接收一个字符串、对象或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 正常的 HTML 特性
attrs: {
id: 'foo'
},
// 组件 props
props: {
myProp: 'bar'
},
// DOM 属性
domProps: {
innerHTML: 'baz'
},
// 事件监听器基于 `on`
// 所以不再支持如 `v-on:keyup.enter` 修饰器
// 需要手动匹配 keyCode。
on: {
click: this.clickHandler
},
// 仅对于组件,用于监听原生事件,而不是组件内部使用
// `vm.$emit` 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
// 赋值,因为 Vue 已经自动为你进行了同步。
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// 作用域插槽格式
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其他组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其他特殊顶层属性
key: 'myKey',
ref: 'myRef'
}
createEmptyVNode方法
createEmptyVNode用于创建一个空的VNode节点
/*创建一个空VNode节点*/
export const createEmptyVNode = () => {
const node = new VNode()
node.text = ''
node.isComment = true
return node
}
createTextVNode方法
createTextVNode用于创建一个文本节点
/*创建一个文本节点*/
export function createTextVNode (val: string | number) {
return new VNode(undefined, undefined, undefined, String(val))
}
cloneVNode方法
cloneVNode方法用于clone一个VNode节点
export function cloneVNode (vnode: VNode): VNode {
const cloned = new VNode(
vnode.tag,
vnode.data,
vnode.children,
vnode.text,
vnode.elm,
vnode.context,
vnode.componentOptions
)
cloned.ns = vnode.ns
cloned.isStatic = vnode.isStatic
cloned.key = vnode.key
cloned.isCloned = true
return cloned
}
以上就是VNode类的定义以及三个操作VNode的基础函数,下一篇博客我们讲一下生成组件VNode的createComponent函数以及统一生成VNode节点的createElement函数