Teleport组件
约 784 字大约 3 分钟
2025-08-11
Teleport
注意这是一个内置组件(不是指令/函数)。作用:将一段模板内容渲染到 DOM 的任意位置,和 React 的 createPortal
类似。
用法
<!-- App.vue -->
<template>
<teleport to="body">
<div>宁采柯</div>
</teleport>
</template>
参数
- slot 内容(children): 要渲染的模板/组件(
<teleport>...</teleport>
中的内容) - to: 必填,要挂载到的 DOM 位置
- 支持 CSS 选择器字符串(如
"body"
,"#app"
,".container"
) - 也支持传入原生 DOM 节点(如
:to="someElement"
)
- 支持 CSS 选择器字符串(如
- disabled?: 可选,布尔值。为
true
时不发生传送,内容按原地渲染 - key?: 可选,常规 VNode
:key
,用于在列表等场景稳定识别节点
返回值/行为
- 渲染结果仍是标准的 Vue 渲染;Teleport 只是把子内容“搬运”到目标 DOM。
- 组件的响应式上下文、事件冒泡、依赖注入等均保持原父组件关系不变。
- 仅影响 DOM 挂载位置,不改变组件的逻辑上下文。
应用场景
- 弹窗
- 下拉框
- 全局提示
- 全局遮罩
- 全局 Loading 例如 UI 库的 Modal/Dropdown 通常会挂载到
body
。
案例:封装弹框组件
src/components/Modal/index.vue
<template>
<teleport to="body">
<div class="modal">
<div class="modal-header">
<div class="modal-title">标题</div>
</div>
<div class="modal-content">
<h1>Modal</h1>
</div>
<div class="modal-footer">
<button class="modal-close-button">关闭</button>
<button class="modal-confirm-button">确定</button>
</div>
</div>
</teleport>
</template>
<script setup lang="ts">
import './index.css'
</script>
src/components/Modal/index.css
.modal {
position: absolute;
top: 0;
left: 0;
width: 500px;
height: 400px;
border: 1px solid #4d4d4d;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 20px;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-title {
font-size: 1.5rem;
font-weight: bold;
}
.modal-content {
padding: 20px 0;
flex: 1;
}
.modal-footer {
display: flex;
justify-content: flex-end;
}
.modal-close-button {
margin-right: 10px;
background-color: #000;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
}
.modal-confirm-button {
margin-left: 10px;
background-color: rgb(46, 46, 164);
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
}
如果外层存在 position: relative
,传统“原地渲染”的弹框会相对外层定位;如果外层没有该样式,则相对 body
定位。这会导致不稳定。为保证稳定,我们有两种方案:
方案一:使用 Teleport(推荐)
<template>
<teleport to="body">
<div class="modal">...</div>
</teleport>
</template>
- 直接将弹框挂载到
body
,不受外层定位上下文影响 - 可灵活切换挂载点:
to="#modal-root"
、to=".overlay-root"
等
方案二:使用 position: fixed
.modal {
position: fixed;
top: 0;
left: 0;
width: 500px;
height: 400px;
border: 1px solid #4d4d4d;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 20px;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
- 一般将相对浏览器视口定位,但若父级设置了
transform
/perspective
/filter
/backdrop-filter
非none
,fixed
可能会相对该父级定位,从而导致偏移不准。
小柯箴言
更推荐使用 Teleport,因为它更灵活且语义清晰,能挂载到任意位置,并避免 position: fixed
在特定 CSS 上下文中的各种坑。fixed
在包含块被改写的场景(如父级 transform
)下容易出现定位不准确的问题。
- 使用 Teleport:结构清晰、上下文不变、定位稳定
- 仅在确有需要时再选择
fixed
,并充分评估包含块影响