Skip to content

Teleport ​

Teleport是一个内置组件,它可以将一个组件内部的一部分模板传送到该组件的DOM结构外层的位置

基本用法 ​

在实际开发,深层组件中写一个Modal弹窗,父容器设置了overflow: hidden或z-index层级冲突,导致弹窗不全或被遮住

如果不用 Teleport,你只能把弹窗放到 App.vue 根节点,然后通过繁琐的 EventBus 或 Pinia 状态来控制显隐。有了 Teleport,你可以直接在组件内部写逻辑,让 Vue 帮你把 DOM 挪出去。

vue
<template>
  <!-- 点击按钮,在组件内部控制弹窗 -->
  <button @click="open = true">打开弹窗</button>

  <!-- Teleport:将弹窗传送到 body 标签下 -->
  <Teleport to="body">
    <div v-if="open" class="modal-overlay">
      <div class="modal-content">
        <p>我是被传送出去的弹窗!</p>
        <button @click="open = false">关闭</button>
      </div>
    </div>
  </Teleport>
</template>

<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>

<style scoped>
/* 这里的样式依然生效,且因为挂载到 body,不易被父级干扰 */
.modal-overlay {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999; /* 现在它脱离了父级层叠上下文,生效更可靠 */
}
</style>

to的值可以是一个CSS选择器字符串(如"body"、"#app"),也可以是一个DOM元素对象

DANGER

传送的目标元素(如 body)必须在 Teleport 挂载时已经存在于 DOM 中。如果目标是 Vue 动态渲染的元素,请确保它已渲染完成,或者配合 disabled 属性使用。

禁用Teleport ​

可以动态传入一个disableprop来处理

vue
<Teleport :disabled="isMobile">
  ...
</Teleport>

然后我们可以动态的更新isMobile

多个Teleport共享目标 ​

一个可重用的 <Modal> 组件可能同时存在多个实例。对于此类场景,多个 <Teleport> 组件可以将其内容挂载在同一个目标元素上,而顺序就是简单的顺次追加,后挂载的将排在目标元素下更后面的位置上,但都在目标元素中。

vue
<Teleport to="#modals">
  <div>A</div>
</Teleport>
<Teleport to="#modals">
  <div>B</div>
</Teleport>

渲染的结果为:

vue
<div id="modals">
  <div>A</div>
  <div>B</div>
</div>