Vue.js 2.x 与 Vue.js 1.x 最大的区别就在于 2.x 使用了 Virtual Dom (虚拟 DOM )来更新 DOM节点,提升擅染性能。
Virtual Dom
React 和 Vue 2 都使用了 Virtual Dom 技术, Virtual Dom 并不是真正意义上的 DOM ,而是一个轻量级的 JavaScript 对象,在状态发生变化时, Virtual Dom 会进行 Diff 运算,来更新只需要被替换的 DOM,而不是全部重绘。
与 DOM 操作相比, Virtual Dom 是基于 JavaScript 计算的,所以开销会小很多 。
用 Virtual Dom 创建的 JavaScript 对象一般会是这样的:
var vNode = { tag:'div', attributes:{ id:'main' }, children:[ //p节点 ] }
在 Vue.js 2 中, Virtual Dom 就是通过一种 VNode 类表达的,每个 DOM 元素或组件都对应一个 VNode 对象,在 Vu巳. 源码中是这样定义的:
export interface VNode { tag?:string;//当前节点的标签名 data?:VNodeData;//当前节点的数据对象 children?:VNode[];//子节点,数组,也是VNnode类型 text?:string;//当前节点的文本,一般文本节点或注释会有该属性 elm?:Node;//当前虚拟节点对应的真实的DOM节点 ns?:string;//节点的namespace context?:Vue; key?:string|number;//节点的key属性,用于作为节点的标识,有利于patch的优化 componentOptions?:VNodeComponentOptions;//创建组件实例时会用到的选项信息 componentInstance?:Vue; parent?:VNode;//组件的占位节点 raw?:boolean;//原始HTML isStatic?:boolean;//静态节点标识 isRootInser:boolean;//是否作为根节点插入,被<transition>包裹的节点,该属性的值为false isComment:boolean;//当前节点是否是注释节点 }
VNodeData 代码如下:
export interface VNodeData { key?:string|number; slot?:string; scopedSlots?:{[key:string]:ScopedSlot}; ref?:string; tag?:string; staticStyle?:{[key:string]:any}; style?:Object[] | Object; props?:{[key:string]:any}; attrs?:{[key:sstring]:any}; domProps?:{[key:string]:any}; hook?:{[key:string]:Function}; on?:{[key:string]:Function|Function[]}; nativeOn?:{[key:string]:Function|Function[]}; transition?:Object; show?:boolean; inlineTemplate?:{ render:Function; staticRenderFns:Function[]; }; directives?:VNodeDirection[]; keepAlive?:boolean; }
主要可分以下几列:
什么是Render函数
Render 函数通过 createElement 参数来创建 Virtual Dom,结构精简了很多。
<div id="app">
<anchor :level="2" title="特性">特性</anchor>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('anchor',{
props:{
level:{
type:Number,
required:true
},title:{
type:String,
default:''
}
},
render:function(createElement){
return createElement(
'h'+this.level,
[
createElement(
'a',
{
domProps:{
href:'#'+this.title
}
},
this.$slots.default
)
]
)
}
});
var app = new Vue({
el:'#app'
})
</script>
createElement 用法
基本参数
createElement(
//{String | Object | Function}
//一个HTML标签,组件选项,或一个函数
//必须Return上述其中一个
'div',
//{Object}
//一个对应属性的数据对象,可选
//可以在template中使用
{
//和v-bind:class一样的API
'class':{
foo:true,
bar:false
},
//和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
},
//自定义指令
directives:[
{
name:'my-custom-directive',
value:'2',
expression:'1+1',
arg:'foo',
modifiers:{
bar:ture
}
}
],
//作用域slot
//{name:props=>VNode|Array<VNode>}
scopedSlots:{
default:props=>h('sspan',props.text)
},
//如果子组件有定义slot的名称
slot:'name-of-slot'
//其他特殊顶层属性
key:'myKey',
ref:'myRef'
},
//{String | Array}
//子节点(VNodes),可选
[
createElement('h1','hello world'),
createElement(MyComponent,{
props:{
someProp:'foo'
}
}),
'bar'
]
)
以往在 template 里 , 我们都是在组件的标签上使用形容 v-bind:class 、 v-bind:s刷e、 v-on:click这样的指令,在 Render 函数都将其写在了数据对象里
传统的 template 写法:
<div id="app">
<ele></ele>
</div>
<script>
Vue.component('ele',{
template:'\
<div id="element"\
:class="{show:show}"\
@click="handleClick">文本内容</div>',
data:function(){
return {
show:true
}
},
methods:{
handleClick:function(){
console.log('clicked!');
}
}
};
var app = new Vue({
el:'#app'
})
</script>
使用 Render 改写后:
<div id="app">
<ele></ele>
</div>
<script>
Vue.component('ele',{
render:function(createElement){
return createElement(
'div',
{
//动态绑定class,同:class
class:{
'show':this.show
},
//普通html属性
attrs:{
id:'element'
},
//给div绑定click事件
on:{
click:this.handleClick
}
},
'文本内容'
)
},
data:function(){
return {
show:true
}
},
methods:{
handleClick:function(){
console.log('clicked!');
}
}
});
var app = new Vue({
el:'#app'
})
</script>