news 2026/6/26 5:29:34

使用 `<Teleport>` 实现全局模态框(Vue 3)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 `<Teleport>` 实现全局模态框(Vue 3)

使用<Teleport>实现全局模态框(Vue 3)

<Teleport>是 Vue 3 内置组件,可以将组件模板的一部分“传送”到 DOM 中任意位置(如<body>),从而突破组件层级限制,非常适合实现模态框、全局提示、下拉菜单等需要脱离父容器样式的场景。

下面实现一个可复用、支持插槽、带有动画的全局模态框组件。


1. 模态框组件:GlobalModal.vue
<template> <!-- 使用 Teleport 将模态框传送到 body --> <Teleport to="body"> <!-- 遮罩层(点击关闭) --> <div v-if="modelValue" class="modal-overlay" @click.self="close"> <!-- 内容容器 --> <div class="modal-container" :class="{ 'modal-enter': isVisible }"> <div class="modal-content"> <!-- 关闭按钮 --> <button class="modal-close" @click="close">✕</button> <!-- 标题插槽 --> <div class="modal-header"> <slot name="header"> <h3>{{ title }}</h3> </slot> </div> <!-- 默认插槽:主体内容 --> <div class="modal-body"> <slot /> </div> <!-- 底部操作插槽 --> <div class="modal-footer" v-if="$slots.footer"> <slot name="footer" /> </div> </div> </div> </div> </Teleport> </template> <script setup> import { watch, nextTick, ref } from 'vue' const props = defineProps({ modelValue: { type: Boolean, default: false }, title: { type: String, default: '提示' }, // 点击遮罩是否关闭 closeOnClickOverlay: { type: Boolean, default: true }, // 是否显示动画(简单过渡) animated: { type: Boolean, default: true } }) const emit = defineEmits(['update:modelValue', 'close', 'open']) // 内部控制动画显示 const isVisible = ref(false) // 监听 modelValue 变化,控制打开/关闭动画 watch( () => props.modelValue, async (newVal) => { if (newVal) { // 打开时,先让元素显示,再触发动画 await nextTick() isVisible.value = true emit('open') // 禁止 body 滚动(避免穿透) document.body.style.overflow = 'hidden' } else { // 关闭动画 isVisible.value = false document.body.style.overflow = '' emit('close') } }, { immediate: true } ) const close = () => { if (props.closeOnClickOverlay) { emit('update:modelValue', 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: 9999; animation: fadeIn 0.3s ease; } /* 内容容器(带动画) */ .modal-container { background: white; border-radius: 12px; max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); transform: scale(0.8); opacity: 0; transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease; } .modal-container.modal-enter { transform: scale(1); opacity: 1; } /* 内容内边距 */ .modal-content { padding: 24px; position: relative; } /* 关闭按钮 */ .modal-close { position: absolute; top: 12px; right: 16px; background: none; border: none; font-size: 22px; line-height: 1; cursor: pointer; color: #999; transition: color 0.2s; } .modal-close:hover { color: #333; } /* 标题 */ .modal-header { margin-bottom: 16px; padding-right: 30px; } .modal-header h3 { margin: 0; font-size: 18px; } /* 主体 */ .modal-body { margin-bottom: 20px; } /* 底部 */ .modal-footer { display: flex; justify-content: flex-end; gap: 10px; border-top: 1px solid #eee; padding-top: 16px; } /* 进入动画(遮罩淡入) */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } </style>

2. 父组件使用示例
<template> <div> <button @click="openModal">打开模态框</button> <!-- 使用模态框,通过 v-model 控制显示 --> <GlobalModal v-model="showModal" title="温馨提示"> <!-- 默认插槽:主体内容 --> <p>这是模态框的内容区域,可以放置任何 Vue 组件或 HTML。</p> <input v-model="inputValue" placeholder="输入一些内容..." /> <p>输入的值:{{ inputValue }}</p> <!-- 底部插槽:自定义按钮 --> <template #footer> <button class="btn-primary" @click="confirm">确认</button> <button class="btn-secondary" @click="showModal = false">取消</button> </template> </GlobalModal> </div> </template> <script setup> import { ref } from 'vue' import GlobalModal from './GlobalModal.vue' const showModal = ref(false) const inputValue = ref('') const openModal = () => { showModal.value = true } const confirm = () => { alert(`确认操作,输入内容:${inputValue.value}`) showModal.value = false } </script> <style> .btn-primary { background: #42b883; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } .btn-secondary { background: #eee; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } </style>

3. 关键技术与注意事项
技术点实现方式说明
传送目标<Teleport to="body">模态框 DOM 将被挂载到<body>下,脱离父组件样式和定位限制。
双向绑定v-model="showModal"组件内通过modelValueprop 和update:modelValue事件实现。
遮罩点击关闭@click.self="close"只有点击遮罩本身(而非其子元素)才触发关闭。
防止滚动穿透document.body.style.overflow = 'hidden'模态框打开时禁用 body 滚动,关闭时恢复。
过渡动画使用 CSS transition + 动态 class通过内部isVisible控制类名,实现平滑打开/关闭。
插槽灵活提供headerdefaultfooter插槽父组件可完全自定义标题、内容和底部按钮。
多个模态框可同时使用多个<GlobalModal>每个实例独立控制,但注意 z-index 堆叠。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 5:29:19

高仕星维生素B能稳固发根吗

高仕星维生素B能稳固发根吗&#xff1f;营养师讲清原理35岁的小张发现&#xff0c;每次洗头掉发一把&#xff0c;地漏都被堵住。他开始认真研究&#xff1a;除了防脱洗发水&#xff0c;还能做什么&#xff1f;作为营养师&#xff0c;我们经常被问这类问题。今天从营养学的角度&…

作者头像 李华
网站建设 2026/6/26 5:29:11

数字孪生+大模型重塑工业产线管控模式

工业产线正经历从“自动化”到“智能化”的跃迁。数字孪生提供了物理世界的实时镜像&#xff0c;大模型则赋予了认知与决策能力。两者融合&#xff0c;正在彻底改写传统的生产管控逻辑&#xff1a;从被动响应到主动预见&#xff0c;从人工决策到智能辅助。下面从五个维度拆解这…

作者头像 李华
网站建设 2026/6/26 5:28:52

Java计算机毕设之基于SpringBoot+Vue的校园潮流音乐在线播放平台设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/26 5:28:45

【课程设计/毕业设计】基于SpringBoot的在线潮流音乐视听服务系统设计与实现 个性化歌单创建与潮流音乐播放系统设计与实现【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/26 5:25:06

Windows、Android、iOS 各自的伟大之处

一、Windows&#xff1a;定义个人电脑时代&#xff0c;通用计算的基石抹平电脑使用门槛&#xff0c;普及 PC 在 Windows 诞生之前&#xff0c;电脑操作依赖晦涩命令行&#xff08;DOS、Unix&#xff09;&#xff0c;只有专业人员能使用。Windows 首创图形窗口、鼠标交互、桌面图…

作者头像 李华