Teleport
<Teleport>
是一个内置组件,允许我们将组件模板的一部分 "传送" 到一个位于该组件DOM层次结构之外的DOM节点。
基本用法
有时我们可能会遇到以下场景:组件模板的一部分在逻辑上属于它,但从视觉角度来看,它应该显示在DOM中的其他位置,在Vue应用程序之外。
这种情况最常见的例子是在构建全屏模态时。理想情况下,我们希望模态的按钮和模态本身位于同一个组件中,因为它们都与模态的打开/关闭状态相关。但这意味着模态将与按钮一起渲染,深度嵌套在应用程序的DOM层次结构中。这可能会在通过CSS定位模态时产生一些棘手的问题。
考虑以下HTML结构。
template
<div class="outer">
<h3>Vue Teleport Example</h3>
<div>
<MyModal />
</div>
</div>
以下是 <MyModal>
的实现
vue
<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button @click="open = true">Open Modal</button>
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</template>
<style scoped>
.modal {
position: fixed;
z-index: 999;
top: 20%;
left: 50%;
width: 300px;
margin-left: -150px;
}
</style>
该组件包含一个 <button>
用于触发模态的打开,一个带有 .modal
类的 <div>
,其中将包含模态的内容和一个按钮来自动关闭。
当在初始HTML结构中使用此组件时,存在一些潜在问题
position: fixed
仅在没有任何祖先元素设置transform
、perspective
或filter
属性时将元素定位到视口相对位置。例如,如果我们打算使用CSS转换来动画化祖先<div class="outer">
,这将破坏模态布局!模态的
z-index
受其容器元素的约束。如果有其他元素与<div class="outer">
相重叠并具有更高的z-index
,它将覆盖我们的模态。
<Teleport>
提供了一种简洁的方法来解决这个问题,通过允许我们跳出嵌套的DOM结构。让我们修改 <MyModal>
以使用 <Teleport>
template
<button @click="open = true">Open Modal</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
<Teleport>
的 to
目标期望一个CSS选择器字符串或一个实际的DOM节点。在这里,我们实际上是告诉Vue "传送 这个模板片段到 body
标签"。
您可以通过点击下面的按钮并使用浏览器开发工具检查 <body>
标签
您可以将 <Teleport>
与 <Transition>
结合使用来创建动画模态框 - 请参阅 此处示例。
技巧
teleport 的 to
目标必须在 <Teleport>
组件挂载时已经在 DOM 中。理想情况下,这应该是一个整个 Vue 应用程序之外的外部元素。如果您要针对由 Vue 渲染的另一个元素,请确保在该元素挂载之前挂载 <Teleport>
。
与组件一起使用
<Teleport>
只会更改渲染的 DOM 结构 - 它不会影响组件的逻辑层次结构。也就是说,如果 <Teleport>
包含一个组件,那么该组件将仍然是包含 <Teleport>
的父组件的逻辑子组件。属性传递和事件发射将继续以相同的方式工作。
这也意味着父组件的注入将按预期工作,并且子组件将在 Vue Devtools 中位于父组件下方,而不是实际内容移动到的位置。
禁用 Teleport
在某些情况下,我们可能希望有条件地禁用 <Teleport>
。例如,我们可能希望将组件作为桌面上的覆盖层渲染,但在移动端上内联渲染。 <Teleport>
支持 disabled
属性,可以动态切换
template
<Teleport :disabled="isMobile">
...
</Teleport>
其中 isMobile
状态可以通过检测媒体查询更改来动态更新。
同一目标上的多个 Teleport
一个常见的用例是可重用的 <Modal>
组件,可能存在多个实例同时活跃。在这种情况下,多个 <Teleport>
组件可以将它们的内容挂载到同一目标元素上。顺序将是简单的追加 -较晚挂载的将位于目标元素中较早挂载的后面。
给定以下用法
template
<Teleport to="#modals">
<div>A</div>
</Teleport>
<Teleport to="#modals">
<div>B</div>
</Teleport>
渲染结果将是
html
<div id="modals">
<div>A</div>
<div>B</div>
</div>
延迟 Teleport
在 Vue 3.5 及以上版本中,我们可以使用 defer
属性将 Teleport 的目标解析延迟到应用程序的其他部分已挂载。这允许 Teleport 针对一个由 Vue 渲染但位于组件树较晚部分的容器元素。
template
<Teleport defer to="#late-div">...</Teleport>
<!-- somewhere later in the template -->
<div id="late-div"></div>
请注意,目标元素必须在与 Teleport 同一挂载/更新周期中渲染 - 即如果 <div>
只在两秒后挂载,Teleport 仍然会报告错误。defer 与 mounted
生命周期钩子的工作方式相似。
相关