当前位置: 首页 > article >正文

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

1. 基本概念

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

1.1 基本语法

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

2. 常见使用场景

2.1 模态框

<!-- 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 通知提示

<!-- 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 上下文菜单

<!-- 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 动态目标

<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 到同一目标

<template>
  <teleport to="#notifications">
    <div class="notification">通知 1</div>
  </teleport>
  
  <teleport to="#notifications">
    <div class="notification">通知 2</div>
  </teleport>
</template>

3.3 条件性传送

<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 全局加载指示器

<!-- 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 图片预览器

<!-- 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 目标元素管理

<!-- 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 组件封装

<!-- 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. 目标元素存在性检查
<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 兼容性
<template>
  <client-only>
    <teleport to="body">
      <div>仅客户端渲染的内容</div>
    </teleport>
  </client-only>
</template>
  1. 清理工作
<script setup>
import { onUnmounted } from 'vue'

onUnmounted(() => {
  // 确保清理所有传送的内容
  const target = document.querySelector('#target')
  if (target) {
    target.innerHTML = ''
  }
})
</script>

http://www.kler.cn/a/521191.html

相关文章:

  • 【Docker】快速部署 Nacos 注册中心
  • 运用python爬虫爬取汽车网站图片并下载,几个汽车网站的示例参考
  • 深入学习Java的线程的生命周期
  • MiniHack:为强化学习研究提供丰富而复杂的环境
  • 重回C语言之老兵重装上阵(十六)C语言可变参数
  • 【MCAL实战】MCU模块配置实践
  • 【python】python基于机器学习与数据分析的二手手机特性关联与分类预测(源码+数据集)【独一无二】
  • Synology 群辉NAS安装(3)环境的准备工作
  • 【2025最新计算机毕业设计】基于SpringBoot+Vue爬虫技术的咖啡与茶饮料文化平台(高质量源码,可定制,提供文档,免费部署到本地)
  • WGCLOUD使用介绍 - 如何监控ActiveMQ和RabbitMQ
  • 在Ubuntu上用Llama Factory命令行微调Qwen2.5的简单过程
  • 【算法】数论基础——约数个数定理、约数和定理 python
  • 2024 开源社年度报告:拥抱开源新生活
  • 消息队列篇--扩展篇--码表及编码解码(理解字符字节和二进制,了解ASCII和Unicode,了解UTF-8和UTF-16,了解字符和二进制等具体转化过程等)
  • DroneXtract:一款针对无人机的网络安全数字取证工具
  • 从Stargate看全球科技变局与中国IT互联网的破局之路
  • 每日 Java 面试题分享【第 13 天】
  • 2025最新 Docker 国内可用镜像源仓库地址(01月02日更新)
  • 【UE】Level、World
  • Android AutoMotive--CarPropertyService
  • AIGC专栏18——EasyAnimateV5.1版本详解 应用Qwen2 VL作为文本编码器,支持轨迹控制与相机镜头控制
  • 【WebRTC - STUN/TURN服务 - COTURN配置】
  • Linux二进制部署K8s集群的平滑升级教程
  • uniapp版本升级
  • 菜鸟开发之多表联合增删改
  • Crewai框架添加日志功能