Vue 3 组件设计:用 Teleport 与动态组件搭一套弹窗与 Toast 体系
test12026-09-130 次阅读
问题背景
弹窗、Toast 这类浮层组件若直接写在业务树里,容易被父级 overflow:hidden、z-index 或 transform 上下文影响而错位。Teleport 可以把渲染结果"传送"到 body 下,脱离当前 DOM 层级;动态组件 <component :is> 则方便管理多个 Toast 的堆叠。
Teleport 实现弹窗
<template>
<button @click="open = true">打开弹窗</button>
<Teleport to="body">
<div v-if="open" class="mask" @click.self="open = false">
<div class="dialog">
<h3>确认操作</h3>
<p>确定要删除吗?</p>
<button @click="open = false">取消</button>
<button @click="confirm">确定</button>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const open = ref(false);
function confirm() {
open.value = false;
}
</script>
动态组件管理 Toast 堆叠
// useToast.ts
import { reactive } from 'vue';
let seq = 0;
export const toasts = reactive<{ id: number; text: string }[]>([]);
export function pushToast(text: string, ttl = 2000) {
const id = ++seq;
toasts.push({ id, text });
setTimeout(() => {
const i = toasts.findIndex((t) => t.id === id);
if (i >= 0) toasts.splice(i, 1);
}, ttl);
}
<!-- ToastHost.vue -->
<Teleport to="body">
<div class="toast-wrap">
<component :is="'div'" v-for="t in toasts" :key="t.id" class="toast">
{{ t.text }}
</component>
</div>
</Teleport>
要点
- Teleport 的 to 目标必须已存在于 DOM,通常指向 body 或一个固定的 #overlay 容器。
- 多个弹窗要共享同一遮罩层时,用 reactive 数组统一管理开关,避免重复挂载。
- 动态组件适合按类型渲染不同浮层;纯文本 Toast 直接用 v-for 更轻量。
T
test1
文章作者
问题背景 弹窗、Toast 这类浮层组件若直接写在业务树里,容易被父级 overflow:hidden、z-index...
- 分类
- 技术
- 发布时间
- 2026-09-13
- 字数
- 约 1571 字
- 阅读
- 0 次
