<Teleport> 将组件的标记渲染到 DOM 中与它在组件树中声明的位置不同的地方 — 但同时保持它在逻辑上是组件的一部分(反应性、props、事件都仍然有效)。
它解决的问题
模态框、工具提示和通知通常需要是 <body> 的直接子元素 以避免 CSS 问题 — 父元素的 overflow: hidden、transform 或 z-index 堆叠上下文可能会裁剪或错位模态框。但在逻辑上,模态框属于触发它的组件。
<template>
<button @click="open = true">Open Modal</button>
<Teleport to="body"> <!-- render this elsewhere... -->
<div v-if="open" class="modal-overlay">
<div class="modal">
<p>I'm declared inside this component...</p>
<p>...but rendered as a child of <body>!</p>
<button @click="open = false">Close</button>
</div>
</div>
</Teleport>
</template>
模态框标记在真实 DOM 中移动为 <body> 的子元素,逃脱任何裁剪/堆叠的父元素 — 但 open、@click 和任何 props 仍然完全反应,因为在逻辑上它仍然是这个组件的一部分。
Without Teleport: With Teleport to="body":
<div style="overflow:hidden"> <body>
<div style="transform:..."> ...
<Modal/> ← clipped/trapped <Modal/> ← free, on top of everything
通过逃脱限制的祖先元素,模态框可以可靠地覆盖整个视口,并以可预测的 z-index 坐在所有其他内容之上。
<Teleport to="#modal-root">...</Teleport> <!-- a specific container -->
<Teleport to="body" :disabled="isMobile">...</Teleport> <!-- conditionally disable -->
Teleport 干净地解决了覆盖层(模态框、工具提示、toast 通知、下拉菜单)被它们的 DOM 祖先元素裁剪或错位的经典 CSS 问题。
它让你在逻辑上属于它的地方编写覆盖层(在触发组件内部,具有完全的反应性),同时在它在 DOM 中需要的地方渲染它(通常是 <body>)。
这是 Vue 的内置等效物,相当于 React 的 Portal — 构建覆盖层 UI 的标准、健壮的方式。