标准答案
- Teleport 解决的是渲染位置问题。组件逻辑仍然写在当前组件里,但 DOM 可以挂到 body 或指定容器下。
- 弹窗、抽屉、Popover、全局 Toast 常用 Teleport,因为这些 UI 不应该被父级 overflow、transform、z-index 或 stacking context 限制。
- Teleport 不会改变组件父子关系。props、emits、provide / inject、响应式状态仍然按原组件树工作。
- 使用 Teleport 时仍要处理焦点管理、滚动锁定、Esc 关闭、点击遮罩关闭和可访问性语义。
- 目标容器要稳定存在。SSR 或条件挂载场景下,要注意客户端和服务端渲染结果是否一致。
题目解析
Teleport 的重点不是让组件变成全局组件,而是让 DOM 离开原来的布局上下文。
弹窗如果留在深层组件里,容易被父容器 overflow hidden 裁剪,或者被局部 z-index 影响。
Teleport 只解决挂载位置,不负责弹窗交互完整性。可访问性和滚动控制仍然要自己或组件库处理。
代码示例
弹窗的触发状态留在当前组件里,弹窗 DOM 挂到 body 下。
Vue
<script setup lang="ts">
import { ref } from 'vue'
const visible = ref(false)
</script>
<template>
<button type="button" @click="visible = true">
打开弹窗
</button>
<Teleport to="body">
<div v-if="visible" class="modal-mask">
<section role="dialog" aria-modal="true">
<h2>确认操作</h2>
<button type="button" @click="visible = false">
关闭
</button>
</section>
</div>
</Teleport>
</template>常见误区
- 认为 Teleport 会改变组件通信关系。它只改变 DOM 挂载位置。
- 用了 Teleport 后忽略焦点恢复和键盘关闭,弹窗对键盘用户不可用。
- 目标容器不存在,导致 Teleport 挂载失败或 SSR hydration 不一致。