vnode虚拟节点就是用一个对象来描述真实的 dom 元素,在Vue.js 中虚拟 DOM 的 JavaScript 对象就是 VNode
例子:用js对象描述一个dom
<div id="container"><p></p></div>
// 下面就描述了上面的dom
let obj = {
tag: 'div',
data: {
id: 'container'
},
children: [{
tag: 'p',
data: {},
children: []
}]
}
render () {
return _c('div', {id: 'container'}, _c('p', {}))
}
vue中这样实现(先将dom转换成ast语法树再生成vnode)
function _c(tag, data, ...children){
let key = data.key;
delete data.key;
children = children.map(child=>{
if(typeof child === 'object'){
return child
}else{
return vnode(undefined,undefined,undefined,undefined,child)
}
})
return vnode(tag,data,key,children);
}
function vnode(tag,data,key,children,text,elm){
return {
tag, // 表示的是当前的标签名
data, // 表示的是当前标签上的属性
key, // 唯一表示用户可能传递,在diff过程中可以提高diff的效率
children, // 属性是vnode的子节点
text, // 属性是文本属性
elm // 真实的dom节点
}
}
let a = _c('div', {id: 'container'}, _c('p', {}, 'hello'), 'word')
console.log(a)
// 输出来的信息
{
tag: 'div',
data: { id: 'container' },
key: undefined,
children: [
{
tag: 'p',
data: {},
key: undefined,
children: [
{
tag: undefined,
data: undefined,
key: undefined,
children: undefined,
text: 'hello',
elm: undefined
}
],
text: undefined,
elm: undefined
},
{
tag: undefined,
data: undefined,
key: undefined,
children: undefined,
text: 'word',
elm: undefined
}
],
text: undefined,
elm: undefined
}
函数的第一次tag就是元素标签,data就是属性,children就是子节点,里面会先对子节点进行判断,看是否是一个对象,如果是对象就直接将该对象赋值给children,如果不是对象(当它不是对象时这时就是一个文本)然后调用一个vnode方法,通过这个方法把让他转成一个对象。最后调用vnode将传入参数输出一个对象,这个对象就能描述这个dom结构。这儿使用递归方法实现。
vue中vnode虚拟dom生成流程
template->ast语法树->通过codegen转化成render函数->内部调用的就是_c方法->vnode
补充说明:
template编译出来的是render函数,render函数中会给你一个createElement,createElement执行之后才是vnode。