vue3中Teleport的用法以及使用场景

1. 基本概念

Teleport 是 Vue3 提供的一个内置组件,它可以将组件的内容传送到 DOM 树的任何位置,而不受组件层级的限制。这在处理模态框、通知、弹出菜单等需要突破组件层级限制的场景中特别有用。

1.1 基本语法

vue 复制代码
<template>
  <teleport to="body">
    <!-- 这里的内容会被传送到 body 标签下 -->
    <div class="modal">
      <!-- 模态框内容 -->
    </div>
  </teleport>
</template>

2. 常见使用场景

2.1 模态框

vue 复制代码
<!-- Modal.vue -->
<template>
  <teleport to="body">
    <div v-if="isOpen" class="modal-overlay">
      <div class="modal">
        <div class="modal-header">
          <h3>{{ title }}</h3>
          <button @click="close">×</button>
        </div>
        <div class="modal-body">
          <slot></slot>
        </div>
        <div class="modal-footer">
          <slot name="footer">
            <button @click="close">关闭</button>
          </slot>
        </div>
      </div>
    </div>
  </teleport>
</template>

<script setup>
const props = defineProps({
  isOpen: Boolean,
  title: String
})

const emit = defineEmits(['update:isOpen'])

const close = () => {
  emit('update:isOpen', false)
}
</script>

<style scoped>
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.modal {
  background: white;
  padding: 20px;
  border-radius: 8px;
  min-width: 300px;
}
</style>

2.2 通知提示

vue 复制代码
<!-- Notification.vue -->
<template>
  <teleport to="#notifications-container">
    <div
      v-if="show"
      :class="['notification', type]"
      @click="close"
    >
      <div class="notification-content">
        {{ message }}
      </div>
    </div>
  </teleport>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const props = defineProps({
  message: String,
  type: {
    type: String,
    default: 'info'
  },
  duration: {
    type: Number,
    default: 3000
  }
})

const show = ref(true)

const close = () => {
  show.value = false
}

onMounted(() => {
  if (props.duration > 0) {
    setTimeout(close, props.duration)
  }
})
</script>

<style scoped>
.notification {
  position: fixed;
  top: 16px;
  right: 16px;
  padding: 12px 24px;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
}

.info {
  background: #e6f7ff;
  border: 1px solid #91d5ff;
}

.success {
  background: #f6ffed;
  border: 1px solid #b7eb8f;
}

.error {
  background: #fff2f0;
  border: 1px solid #ffccc7;
}
</style>

2.3 上下文菜单

vue 复制代码
<!-- ContextMenu.vue -->
<template>
  <teleport to="body">
    <div
      v-if="show"
      class="context-menu"
      :style="position"
    >
      <slot></slot>
    </div>
  </teleport>
</template>

<script setup>
import { ref, computed } from 'vue'

const props = defineProps({
  show: Boolean,
  x: Number,
  y: Number
})

const position = computed(() => ({
  left: props.x + 'px',
  top: props.y + 'px'
}))
</script>

<style scoped>
.context-menu {
  position: fixed;
  background: white;
  border: 1px solid #eee;
  border-radius: 4px;
  padding: 8px 0;
  min-width: 160px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
</style>

3. 高级用法

3.1 动态目标

vue 复制代码
<template>
  <teleport :to="target">
    <div class="content">
      动态传送的内容
    </div>
  </teleport>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const target = ref('body')

onMounted(() => {
  // 可以根据条件动态改变目标
  if (window.innerWidth < 768) {
    target.value = '#mobile-container'
  }
})
</script>

3.2 多个 Teleport 到同一目标

vue 复制代码
<template>
  <teleport to="#notifications">
    <div class="notification">通知 1</div>
  </teleport>
  
  <teleport to="#notifications">
    <div class="notification">通知 2</div>
  </teleport>
</template>

3.3 条件性传送

vue 复制代码
<template>
  <teleport to="body" :disabled="isMobile">
    <div class="modal">
      <!-- 在移动端不会被传送,保持原位置 -->
    </div>
  </teleport>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const isMobile = ref(false)

onMounted(() => {
  isMobile.value = window.innerWidth < 768
  
  window.addEventListener('resize', () => {
    isMobile.value = window.innerWidth < 768
  })
})
</script>

4. 实际应用示例

4.1 全局加载指示器

vue 复制代码
<!-- LoadingIndicator.vue -->
<template>
  <teleport to="body">
    <div v-if="loading" class="loading-overlay">
      <div class="loading-spinner"></div>
      <div class="loading-text">{{ message }}</div>
    </div>
  </teleport>
</template>

<script setup>
defineProps({
  loading: Boolean,
  message: {
    type: String,
    default: '加载中...'
  }
})
</script>

<style scoped>
.loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, 0.9);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}
</style>

4.2 图片预览器

vue 复制代码
<!-- ImageViewer.vue -->
<template>
  <teleport to="body">
    <div
      v-if="visible"
      class="image-viewer"
      @click="close"
    >
      <img
        :src="imageUrl"
        @click.stop
      >
      <div class="controls">
        <button @click.stop="prev">&lt;</button>
        <button @click.stop="next">&gt;</button>
      </div>
    </div>
  </teleport>
</template>

<script setup>
const props = defineProps({
  visible: Boolean,
  imageUrl: String,
  images: Array
})

const emit = defineEmits(['update:visible'])

const close = () => {
  emit('update:visible', false)
}

const prev = () => {
  // 实现上一张逻辑
}

const next = () => {
  // 实现下一张逻辑
}
</script>

5. 最佳实践

5.1 目标元素管理

html 复制代码
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Vue App</title>
</head>
<body>
  <div id="app"></div>
  
  <!-- 为 Teleport 预留的容器 -->
  <div id="modals"></div>
  <div id="notifications"></div>
  <div id="tooltips"></div>
</body>
</html>

5.2 组件封装

vue 复制代码
<!-- BaseModal.vue -->
<template>
  <teleport to="#modals">
    <transition name="modal">
      <div
        v-if="modelValue"
        class="modal-container"
        @click.self="close"
      >
        <div class="modal-content">
          <slot></slot>
        </div>
      </div>
    </transition>
  </teleport>
</template>

<script setup>
defineProps({
  modelValue: Boolean
})

const emit = defineEmits(['update:modelValue'])

const close = () => {
  emit('update:modelValue', false)
}
</script>

6. 注意事项

  1. 目标元素存在性检查
vue 复制代码
<template>
  <teleport to="#target" :disabled="!targetExists">
    <div>内容</div>
  </teleport>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const targetExists = ref(false)

onMounted(() => {
  targetExists.value = !!document.querySelector('#target')
})
</script>
  1. SSR 兼容性
vue 复制代码
<template>
  <client-only>
    <teleport to="body">
      <div>仅客户端渲染的内容</div>
    </teleport>
  </client-only>
</template>
  1. 清理工作
vue 复制代码
<script setup>
import { onUnmounted } from 'vue'

onUnmounted(() => {
  // 确保清理所有传送的内容
  const target = document.querySelector('#target')
  if (target) {
    target.innerHTML = ''
  }
})
</script>
相关推荐
2501_9444480029 分钟前
Flutter for OpenHarmony衣橱管家App实战:支持我们功能实现
android·javascript·flutter
人工智能训练6 小时前
【极速部署】Ubuntu24.04+CUDA13.0 玩转 VLLM 0.15.0:预编译 Wheel 包 GPU 版安装全攻略
运维·前端·人工智能·python·ai编程·cuda·vllm
会跑的葫芦怪7 小时前
若依Vue 项目多子路径配置
前端·javascript·vue.js
xiaoqi9227 小时前
React Native鸿蒙跨平台如何进行狗狗领养中心,实现基于唯一标识的事件透传方式是移动端列表开发的通用规范
javascript·react native·react.js·ecmascript·harmonyos
jin1233228 小时前
React Native鸿蒙跨平台剧本杀组队消息与快捷入口组件,包含消息列表展示、快捷入口管理、快捷操作触发和消息详情预览四大核心功能
javascript·react native·react.js·ecmascript·harmonyos
烬头88219 小时前
React Native鸿蒙跨平台实现二维码联系人APP(QRCodeContactApp)
javascript·react native·react.js·ecmascript·harmonyos
pas1369 小时前
40-mini-vue 实现三种联合类型
前端·javascript·vue.js
摇滚侠9 小时前
2 小时快速入门 ES6 基础视频教程
前端·ecmascript·es6
2601_9498333910 小时前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter
珑墨10 小时前
【Turbo】使用介绍
前端