Vue鼓励我们通过将UI和相关行为封装到组件中来构建UI,组件之间可以嵌套,从而构成一个UI树。然而,有时候组件模板的一部分在逻辑上属于该组件,但从技术角度来看,应该将模板的这一部分移到DOM中的其他地方,位于Vue应用程序实例之外。
一个常见的场景是创建一个包含全屏模态的组件。在大多数情况下,模态的逻辑都是存在于组件中的,但是你会发现,模态的定位很难通过CSS来解决,使得你不得不考虑对组件进行拆分。
Vue 3官网给出了一个例子,有如下的HTML结构:
<body>
<div style="position: relative;">
<h3>Tooltips with Vue 3 Teleport</h3>
<div>
<modal-button></modal-button>
</div>
</div>
</body>
modal-button组件在嵌套很深的div元素中渲染。modal-button组件的代码如下:
const app = Vue.createApp({});
app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal!
</button>
<div v-if="modalOpen" class="modal">
<div>
I'm a modal!
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
`,
data() {
return {
modalOpen: false
}
}
})
modal-button组件有一个<button>元素来触发模态的打开,以及一个具有.modal样式类的div元素,它包含模态的内容和一个用于自我关闭的按钮。
.modal样式类使用了一个样式表属性:“position: absolute;”,当modal-button组件在上面的HTML结构中渲染时,你会发现一个问题,由于模态在嵌套很深的div中渲染,样式属性position: absolute将相对于父级div元素来应用。为了解决这个问题,Vue 3给出了一个内置组件teleport,该组件允许我们控制在DOM中的哪个父节点下渲染HTML片段。
teleport组件有两个prop,如下所示:
to:字符串类型,必需的prop。其值必须是有效的查询选择器或者HTML的元素名(如果在浏览器的环境中使用)。teleport组件的内容将被移动到指定的目标元素中。disabled:布尔类型,可选的prop。disabled可以用于禁用teleport组件的功能,这意味着它的插槽内容将不会被移动到任何位置,而是在周围父组件中指定<teleport>的地方渲染。修改modal-button组件的代码,使用<teleport>来告诉Vue“将这个HTML传送到body标签下”,代码如下所示:
app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
`,
data() {
return {
modalOpen: false
}
}
})
现在,当我们单击“Open full screen modal! (With teleport!)”按钮,Vue会正确地将模态的内容在body标签下渲染。
如果<teleport>的内容中包含了Vue组件,那么该组件在逻辑上仍然是<teleport>父组件的子组件。我们看下面的代码:
const app = Vue.createApp({
template: `
<h1>Root instance</h1>
<parent-component />
`
})
app.component('parent-component', {
template: `
<h2>This is a parent component</h2>
<teleport to="#endofbody">
<child-component name="John" />
</teleport>
`
})
app.component('child-component', {
props: ['name'],
template: `
<div>Hello, {{ name }}</div>
`
})
不管child-component组件在什么位置渲染,它仍将是parent-component组件的子组件,并从父组件接收name prop。这意味着来自父组件的注入将按预期工作,并且子组件将嵌套在Vue Devtools中父组件的之下,而不是放在实际内容移动到的位置。
一个常见的用例场景是一个可重用的<Modal>组件,其中可能同时有多个活动实例。对于这种情况,多个<teleport>组件可以将它们的内容挂载到同一个目标元素下。挂载顺序将是一个简单的追加,在目标元素中,后挂载的将位于先挂载的之后。
我们看下面的代码:
<teleport to="#modals">
<div>A</div>
</teleport>
<teleport to="#modals">
<div>B</div>
</teleport>
<!-- 结果-->
<div id="modals">
<div>A</div>
<div>B</div>
</div>
我是专注于软件开发和IT教育的孙鑫老师,喜欢我的文章欢迎关注、转发、评论、点赞和收藏,我会经常与大家分享IT技术、编程语言的文章和教学视频。目前已发布完整的《Vue.js从入门到实战》教学视频,正在发布《Java无难事》教学视频。
Vue.js从入门到实战
Vue 3的六大亮点#Vue3#