虚拟DOM技术(Virtual DOM,VDOM)
VDOM的第一个目的:完成在Vue下实现DOM操作的功能。
VDOM的第二个目的:实现比原生的DOM操作还要快的页面渲染机制。
VDOM的本质:利用js对象模拟DOM节点。
一、利用render函数代替组件中的template:
1、render函数的功能:在创建组件是实现DOM结构的生产,和template完全一致。
2、格式:
Vue.component(‘my-com’,{
render:function(createElement){
return createElement(‘div’);
}
//相当于 template:`<div></div>`
})
使用组件:<my-com></my-com>
3、简化格式:
Vue.component(‘my-com’,{
render:h=>h(‘div’)
})
4、创建一个带有文本内容的VDOM节点:
<div>我天上课之前画了一个满之慧</div>
render:h=>h(‘div’,‘我天上课之前画了一个满之慧’)
5、创建一个VDOM节点:
带有名为box的类名,内联样式规定了宽度、高度、背景颜色,具备id属性和title属性。
能够为这个VDOM节点绑定鼠标经过和鼠标离开的事件。
<div class=“box” style=“width:5px;height:5px;background-color:red” id=“” title=“”
onmouseover=“” onmousemove=“”>
```
解决方案:使用h()函数的第二个参数,参数类型为{}。
格式:
h(‘tagName’,{
class:{ //为VDOM节点设置类名 },
style:{ //为VDOM节点设置内联样式},
attrs:{ //为VDOM节点设置HTML属性},
on:{ //为VDOM节点绑定事件},
domProps:{ //为VDOM节点设置JavaScript中的节点属性}
})
例如:
render:h=>h('div',{
class:{box:true},
style:{width:'200px',height:'100px',backgroundColor:'#3385ff'},
attrs:{id:'box',title:'鼠标经过显示文本'},
on:{
mouseover:()=>event.target.style.backgroundColor='#ff5857',
mouseout:()=>event.target.style.backgroundColor='#3385ff'
},
domProps:{
innerText:'我是通过h函数生成的VDOM节点',
contentEditable:true
}
})
6、创建一个带有子节点的VDOM节点:
格式:
`h(‘div’,[h(),h()])`
例如:
render:h=>h('div',{
class:{box:true}
},[
h('h1','我是一级标题'),
h('div',[
h('p','我是一个段落'),
h('a',{
attrs:{href:'#'}
},'百度百科')
])
])
二、根据现有组件创建VDOM节点:
1、例1:根据现有组件my-book创建VDOM节点。
Vue.component(‘my-book’,{
template:`<div class=“book”>书名</div>`
})
Vue.component(‘my-com’,{
render:h=>h(‘my-book’)
})
2、例2:根据带有插槽的现有组件创建VDOM节点。
Vue.component('my-book',{
template:`<div class="book"><slot></slot></div>`
})
Vue.component('my-com',{
// render:h=>h(‘my-book’,‘书名作者’)
render:h=>h('my-book',[h('div','我和我的VUE')])
})
3、例3:根据带有参数的现有组件创建VDOM节点。
Vue.component('my-book',{
template:`
<div class="book">
<p>书名:{{bookName}}</p>
<p>作者:{{author}}</p>
<p>出版社:{{publish}}</p>
</div>
`,
props:['bookName','author','publish']
})
Vue.component('my-com',{
render:h=>h('my-book',{
props:{
bookName:'满之慧与伏地魔',
author:'裴嘉靖',
publish:'金梅出版社'
}
})
})
4、例4:让现有组件作为VDOM节点的子节点。
render:h=>h('div',[
h('h1','2020年畅销书'),
h('my-book',{
props:{
bookName:'满之慧与伏地魔',
author:'裴嘉靖',
publish:'金梅出版社'
}
})
])
三、创建具备插槽的VDOM节点:
注意:具备插槽的VDOM节点不能使用箭头函数。
1、例1:创建具备一个匿名插槽的VDOM节点。
DOM结构格式:
template:`<div><slot></slot></div>`
render(h){
return h(‘div’,this.$slots.default);
}
使用:
<my-com>我被放入匿名插槽中</my-com>
(1)创建一个下列格式的VDOM节点:
<div><span style=“color:#ff5857;”><slot></slot></span></div>
render(h){
return h('div',[
h('span',{
style:{color:'#ff5857'}
},this.$slots.default)
])
}
(2)创建一个超级链接的VDOM节点。
template:`<div><a :href=“url”><slot></slot></a></div>`
render(h){
return h('div',[
h('a',{
attrs:{href:this.url}
},this.$slots.default)
])
},
props:['url']
使用:<my-com url=“https://www.baidu.com”>百度百科</my-com>
2、例2:创建具备多个具名插槽的VDOM节点。
template:`
<div>
<h1><slot name=“abc”></slot></h1>
<h2><slot name=“xyz”></slot></h2>
</div>
`
render(h){
return h('div',[
h('h1',this.$slots.abc),
h('h2',this.$slots.xyz)
])
}
使用:<my-com>
<span slot="abc">哈利波特与魔法石</span>
<span slot="xyz">JK罗琳</span>
</my-com>
3、例3:创建具备一个默认作用域插槽的VDOM节点。
render(h){
return h('div',this.$scopedSlots.default({
a:100,
b:200,
c:50
}))
}
<my-com>
<template slot-scope="sc">
{{sc.a}}+{{sc.b}}+{{sc.c}}={{sc.a+sc.b+sc.c}}
</template>
</my-com>
4、例4:创建具备多个具名的作用域插槽的VDOM节点。
render(h){
return h('div',[
h('h1',this.$scopedSlots.abc({a:100,b:200})),
h('h2',this.$scopedSlots.xyz({x:'A',y:'B'}))
])
}
使用:<my-com>
<template slot="abc" slot-scope="sc">
{{sc.a}}+{{sc.b}}={{sc.a+sc.b}}
</template>
<template slot="xyz" slot-scope="sc">
{{sc.x}}+{{sc.y}}
</template>
</my-com>
四、在render函数中使用Vue指令:
1、利用render函数实现的组件模板不能使用vue指令。
2、例:当list数组中的元素>3个时,遍历出数组的所有内容并显示出来,否则提示内容不
足。
(1)使用传统的template来创建组件。
Vue.component('my-com',{
template:`
<div class="com">
<div v-if="list.length>3">
<ul>
<li v-for="(item,index) in list">{{item}}</li>
</ul>
</div>
<div v-else>
<p>数据内容不足</p>
</div>
</div>
`,
props:['list']
})
(2)使用render函数来创建VDOM节点。
Vue.component('my-com',{
render(h){
let vnode;
if(this.list.length>3){
//创建循环遍历的VDOM节点
vnode=h('ul',this.list.map(item=>h('li',item)));
}else{
//创建提示数据不足的VDOM节点
vnode=h('p','数据内容不足');
}
return h('div',{
class:{com:true}
},[vnode]);
},
props:['list']
})
五、在element-ui中使用render函数:
1、在element-ui的表格组件中,具备一个属性 render-header,该属性的功
能是返回列表头的内容。
(1)可以返回一个字符串作为表头的新标签内容。
(2)可以返回一个VDOM节点作为表头的内容。
2、如何在VDOM中为表单元素绑定数据(VDOM不能使用v-model指令):
在表单元素的input事件中,让表单元素的value属性的取值赋给一个侦听属性。
3、案例:
<template>
<div class="hello">
<el-table :data="tableData" style="width: 100%" :border="true">
<el-table-column prop="date" label="日期" min-width="20%" align="center">
</el-table-column>
<el-table-column :render-header="renderHeader" prop="name" label="姓名" min-width="60%" align="center">
</el-table-column>
<el-table-column prop="address" label="地址" align="center">
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
nameValue:'',
tableData:[], //表格显示的数据
allData:[ //原始的所有数据
{
date:'2020-5-4',
name:'张金梅',
address:'河北省石家庄市'
},
{
date:'2020-6-1',
name:'裴嘉靖',
address:'河北省邯郸市'
},
{
date:'2020-7-1',
name:'满之慧',
address:'山东省'
}
]
}
},
mounted(){
this.tableData=this.allData;
},
methods:{
renderHeader(h){
let that=this;
return h('div',{
on:{
click:function(){
//让子节点span消失,让子节点input显示
let spanNode=document.querySelector('.span');
let inputNode=document.querySelector('.input');
spanNode.style.display='none';
inputNode.style.display="inline-block";
inputNode.focus();
}
}
},[
h('span',{
class:{span:true}
},'姓名'),
h('input',{
class:{input:true},
style:{display:'none'},
on:{
blur(){
let spanNode=document.querySelector('.span');
let inputNode=document.querySelector('.input');
spanNode.style.display="inline";
inputNode.style.display="none";
},
input(){
that.nameValue=event.target.value;
}
}
})
])
}
},
watch:{
nameValue(newValue){
this.tableData=this.allData.filter(item=>{
return item.name.indexOf(newValue)!==-1;
})
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
认识一个开源的VDOM项目:Snabbdom
1、打包工具:parcel
安装 npm install -g parcel-bundler
执行项目:parcel index.html
打包:parcel build index.html
2、下载snabbdom
npm install snabbdom@0.7.4
snabbdom是使用TypeScript来进行书写的。
3、引入snabbdom:
ES6:import from
CommonJS:require()
例:import {h,init} from ‘snabbdom’
4、使用到的函数:
init():参数是数组,返回值为patch()函数。
patch():将生成的VDOM节点放入到挂载点的DOM结构内部。
h():生成VDOM节点。
例1:利用Snabbdom生成一个div节点,并放在HTML的结构中,实现Hell,Snabbdomo
功能。
import {h,init} from ‘snabbdom’;
let patch=init([]);
let vnode=h(‘div’,‘Hello,Snabbdom!’);
let app=document.querySelector(‘#app’);
patch(app,vnode);