Teleport 传送门及render函数
Teleport 传送门
功能
将组件或者组件中的内容挂载到其他DOM的位置上
实现一个需求
给绝对定位的元素加上覆盖整个页面的蒙层mask,由于绝对定位的元素只能相对于其父元素,如果父元素的面积很小,就做不到全屏的mask,因此需要将这个绝对定位的元素挂到body或者其他div上
1. 使用Teleport 传送门之前
【敲代码】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Teleport 传送门</title>
<style>
.area {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
width: 200px;
height: 300px;
background: green;
}
.mask{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, .5);
}
</style>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods:{
handleBtnClick(){
this.show = !this.show
}
},
template: `
<div class="area">
<button @click="handleBtnClick">按钮</button>
<div class="mask" v-show="show"></div>
</div>
`
})
const vm = app.mount('#root')
</script>
</body>
</html>
【运行结果】
结果:由于绝对定位的mask的父元素为绿色背景的area,mask中的样式top left right bottom
只能作用于父元素上,因此mask的范围就只能限定于它的父元素area中,但是实际是想让mask作用于整个页面
因此如果mask可以放到body中或者其他div上就好了
难道要操作DOM吗?
不用!Vue为我们提供了Teleport 传送门功能
2. 使用Teleport 传送门之后
【敲代码】
// 相同代码已省略
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods:{
handleBtnClick(){
this.show = !this.show
}
},
template: `
<div class="area">
<button @click="handleBtnClick">按钮</button>
<teleport to="body">
<div class="mask" v-show="show"></div>
</teloport>
</div>
`
})
const vm = app.mount('#root')
【运行结果】
我们可以发现,mask已经放到body中了
【备注】
<div class="area">
<button @click="handleBtnClick">按钮</button>
<teleport to="body">
<div class="mask" v-show="show"></div>
</teloport>
</div>
-
这段代码的意思是渲染的时候就不放在class为area的标签里,而是传送到body标签中
-
当然不仅仅可以传到body中,可以传到任何的div中,如:
<teleport to="#wrapper">
<div class="mask" v-show="show"></div>
</teloport>
- 传送门除了可以将页面中的元素放到指定的DOM元素中外,其他的逻辑和写组件是一样的,作用域还是在当前的组件中
render函数
【敲代码】
1. 让插槽中的字重为h1
const app = Vue.createApp({
template: `
<my-title>
Hello World!
</my-title>
`
})
app.component('my-title', {
template: `
<h1>
<slot />
</h1>
`
})
const vm = app.mount('#root')
【运行结果】
2. 如何让字重受到外部参数的控制?
给子组件传值?
【敲代码】
const app = Vue.createApp({
template: `
<my-title :level="2">
Hello World!
</my-title>
`
})
app.component('my-title', {
props:['level'],
template: `
<h1 v-if="level === 1"><slot /></h1>
<h2 v-if="level === 2"><slot /></h2>
<h3 v-if="level === 3"><slot /></h3>
`
})
const vm = app.mount('#root')
通过这种方式展示不同自重,代码显得不够优雅
3. 子组件中使用vue提供的render函数替代模板template
【敲代码】
const app = Vue.createApp({
template: `
<my-title :level="1">
Hello World!
</my-title>
`
})
app.component('my-title', {
props:['level'],
render(){
const { h } = Vue
return h('h' + this.level,{},this.$slots.default())
}
})
const vm = app.mount('#root')
【备注】
this.$slots.default()
:表示使用插槽中的内容
3.render函数和模板template
之间存在的关系是什么?
虚拟DOM:
// 真实DOM
<div>hello</div>
// render函数
return h('div',{},'hello')
// 虚拟DOM: 通过JS对象表述这个div标签
{
tagName: 'div',
attributes: {},
text: 'hello',
}
再来看下例子中render函数的返回值(即虚拟DOM)
return h('h' + this.level,{},this.$slots.default())
【备注】
- 第一个参数
'h' + this.level
对应的就是tagName
- 第二个参数对应
attributes
,即标签上的属性,如<div a=1>hello</div>
,如果有就是{a:1}
否则就是空对象{}
- 第三个参数
this.$slots.default()
对应的就是虚拟DOM中标签的文本内容text
Vue里面把template变成render函数,render函数再去返回一个虚拟DOM的好处
- 可以让Vue的性能更快
- 可以让Vue具备跨平台的能力
【备注】
1.template ==> render函数(被解析成) ==> 调用h函数 ==> h函数生成虚拟DOM(JS对象) ==> (映射成)真实DOM ==> 展示到页面上
2.上面例子就是跳过将template转成render函数,而是直接写render函数
h函数的第三个参数还可以是一个数组,可以继续嵌套虚拟DOM
【敲代码】
render(){
const { h } = Vue
return h('h' + this.level,{},[
this.$slots.default(),
h('h3',{},'我是嵌套在h1标签中的h3标签中的内容')
])
}
【运行结果】
当然每一个h函数的第三个参数都可以写成数组的形式,生成无限嵌套的JS对象,从而生成无限嵌套的虚拟DOM,最终生成嵌套的DOM节点展示到页面上
转载自:https://juejin.cn/post/7244470938871529530