Vue3 自定义 Toast 组件封装实践
前言
在前端开发中,Toast 提示是一个非常常用的交互组件。本文将介绍如何在 Vue3 中封装一个功能完整、可复用的 Toast 组件。
功能特点
- 支持多种提示类型(success/error/warning/default)
- 自定义显示时长
- 防抖处理,避免频繁调用
- 优雅的动画效果
- 自动清理资源
效果展示:
默认效果:
失败效果:
成功效果:
警告效果:
实现原理
1. 组件结构
Toast 组件采用 Vue3 组合式 API 实现,主要包含三个部分:
- MyToast.vue:Toast 组件本身
- toast.js:Toast 插件封装
- 全局注册和使用
2. 核心代码实现
2.1 Toast 组件 (MyToast.vue)
组件特点:
- 使用 transition 实现淡入淡出动画
- 支持不同类型的图标显示
- 自适应内容宽度
- 居中显示设计
<template>
<transition name="fade">
<div v-if="visible" class="my-toast" :class="type">
<div class="toast-content">
<iconpark-icon
v-if="type !== 'default'"
:name="iconName"
size="16px"
:class="['toast-icon', type]"
/>
<span>{{ message }}</span>
</div>
</div>
</transition>
</template>
<script setup>
// 组件逻辑实现
const props = defineProps({
message: String,
type: String,
duration: Number
});
const visible = ref(false);
let timer = null;
// 显示方法
const show = () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
visible.value = true;
if (props.duration > 0) {
timer = setTimeout(() => {
visible.value = false;
timer = null;
}, props.duration);
}
};
// 资源清理
onBeforeUnmount(() => {
if (timer) {
clearTimeout(timer);
timer = null;
}
});
</script>
样式:
<style lang="less" scoped>
.my-toast {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
padding: 8px 16px;
border-radius: 4px;
background: rgba(0, 0, 0, 0.7);
color: #fff;
font-size: 14px;
line-height: 20px;
max-width: 80%;
.toast-content {
display: flex;
align-items: center;
gap: 4px;
}
.success {
color: #52c41a;
}
.error {
color: #ff4d4f;
}
.warning {
color: #faad14;
}
}
// 动画效果
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
2.2 Toast 插件封装 (toast.js)
import { createVNode, render } from 'vue';
import MyToast from '@/components/MyToast.vue';
const toast = {
install(app) {
// 创建一个容器
const container = document.createElement('div');
document.body.appendChild(container);
// 用于存储定时器
let timer = null;
// 创建显示方法
const showToast = ({ message = '', type = 'default', duration = 2000 }) => {
// 如果存在之前的定时器,清除它
if (timer) {
clearTimeout(timer);
timer = null;
}
// 确保容器存在
if (!document.body.contains(container)) {
document.body.appendChild(container);
}
// 清除已有的 toast
render(null, container);
// 创建新的 toast
const vnode = createVNode(MyToast, {
message,
type,
duration,
});
// 渲染到容器
render(vnode, container);
// 显示 toast
vnode.component?.exposed?.show();
// 设置新的定时器
timer = setTimeout(() => {
if (document.body.contains(container)) {
render(null, container);
}
timer = null;
}, duration + 300); // 加300ms确保动画完成
};
// 防抖函数
const debounce = (fn, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
};
// 注册全局方法
app.config.globalProperties.$toast = {
show: debounce((message) => showToast({ message }), 300),
success: debounce((message) => showToast({ message, type: 'success' }), 300),
error: debounce((message) => showToast({ message, type: 'error' }), 300),
warning: debounce((message) => showToast({ message, type: 'warning' }), 300),
};
// 在应用卸载时清理
app.config.globalProperties.$unmounted = () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
if (document.body.contains(container)) {
render(null, container);
document.body.removeChild(container);
}
};
},
};
export default toast;
3. 关键技术点解析
3.1 组件渲染机制
- 使用
createVNode创建虚拟节点 - 通过
render函数渲染到 DOM - 利用
transition组件实现动画效果
3.2 防抖处理
// 防抖函数
const debounce = (fn, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
};
3.3 资源管理
- 组件级别的定时器清理
- 插件级别的容器管理
- 应用卸载时的资源回收
使用方式
1. 全局注册
import toast from './plugins/toast';
const app = createApp(App);
app.use(toast);
2. 组件中使用
const { proxy } = getCurrentInstance();
// 基础提示
proxy.$toast.show('这是一条提示');
// 成功提示
proxy.$toast.success('操作成功');
// 错误提示
proxy.$toast.error('操作失败');
// 警告提示
proxy.$toast.warning('请注意');
实现要点
1. 单例模式
- 确保同一时间只显示一个 Toast
- 新的 Toast 会替换旧的 Toast
2. 防抖处理
- 避免频繁创建和销毁组件
- 提升性能和用户体验
3. 资源管理
- 及时清理定时器
- 组件卸载时清理 DOM
- 防止内存泄漏
4. 样式设计
- 使用 fixed 定位居中显示
- 添加过渡动画提升体验
- 支持多种类型的样式定制
优化建议
-
性能优化
- 使用 CSS transform 代替位置属性
- 合理使用防抖控制调用频率
-
功能扩展
- 支持 HTML 内容
- 支持自定义位置
- 支持队列显示
-
可访问性
- 添加 ARIA 属性
- 支持键盘操作
总结
通过封装 Toast 组件,我们不仅实现了一个实用的提示组件,还学习了:
- Vue3 组件封装的最佳实践
- 插件开发和全局注册
- 性能优化和资源管理
- 组件通信和状态管理