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 次