Vue 3 Message 组件封装
本文将介绍如何封装一个简洁且灵活的消息提示组件,支持多种类型的消息展示、自动消失、手动关闭等功能,并且提供了一些可配置的选项,如持续时间、偏移量、关闭按钮等。该组件使用了 Vue 3 的功能,包括 Transition、ref、computed、defineProps 和 defineExpose,并且通过动态创建和销毁组件实例的方式,来实现消息提示的展示。
1. 功能需求
我们需要实现一个 Message 组件,它能够满足以下功能:
- 消息类型:支持
success、info、warning、error四种类型的消息,显示不同的样式。 - 自动消失:消息提示展示一段时间后自动消失。
- 手动关闭:提供关闭按钮,用户可以手动关闭消息。
- 自定义配置:用户可以自定义消息内容、持续时间、偏移量、zIndex 等。
- 支持 HTML 内容:支持将 HTML 字符串作为消息内容。
2. 组件实现
2.1 Message 组件模板
<template>
<Transition name="message-fade" @before-leave="beforeLeave" @after-leave="emit('destroy')">
<div v-show="visible" :id="id" :class="[n(), type && n('--' + type)]" :style="styles">
<i class="iconfont icon-chenggong" style="font-size: 20px"></i>
<div v-if="!dangerouslyUseHTMLString" :class="n('content')">
<slot>{{ message }}</slot>
</div>
<div v-else :class="n('content')" v-html="message"></div>
<div v-if="showClose" :class="n('closeBtn')" @click="close">
<i class="iconfont icon-close"></i>
</div>
</div>
</Transition>
</template>
- Transition:用来控制消息组件的进出场动画。
- v-show:用于显示或隐藏消息。
- :class:动态绑定类名,支持根据消息类型(success、info、warning、error)来设置不同的样式。
- v-html:支持渲染 HTML 内容。
- 关闭按钮:点击后触发
close方法,手动关闭消息。
2.2 Message 组件的逻辑部分
<script lang="ts" setup>
import { onMounted, onBeforeMount, ref, computed } from 'vue'
import { createNamespace } from '../utils'
import { MessageProps } from './message'
defineOptions({
name: 'VanMessage'
})
const props = defineProps(MessageProps)
const emit = defineEmits(['destroy', 'close'])
const { n } = createNamespace('message')
const visible = ref(false)
const timerId = ref()
const styles = computed(() => ({
top: props.offset + 'px',
zIndex: props.zIndex
}))
const startTimer = () => {
timerId.value = setTimeout(() => {
visible.value = false
}, props.duration)
}
const clearTimer = () => {
if (timerId.value) {
clearTimeout(timerId.value)
timerId.value = undefined
}
}
const close = () => visible.value = false
const beforeLeave = () => {
emit('close')
}
onMounted(() => {
startTimer()
visible.value = true
})
onBeforeMount(() => {
clearTimer()
})
defineExpose({
visible,
close
})
</script>
startTimer:在组件挂载时启动定时器,设置消息的自动消失时间。clearTimer:在组件销毁前清除定时器,防止内存泄漏。close:手动关闭消息,改变visible状态。beforeLeave:在消息消失前,触发close事件,执行关闭操作。
2.3 样式部分
<style lang="less">
@import './message.less';
</style>
message.less 文件包含了消息提示的基本样式,包括背景颜色、文字颜色、圆角、阴影等。你可以根据自己的需求进一步修改这些样式。
3. 消息提示的全局管理
为了能够全局管理消息提示,我们通过动态创建 Message 组件的实例,并将其渲染到 DOM 中。
3.1 全局消息函数实现
import { AppContext, createVNode, render, isVNode, VNode, ComponentPublicInstance } from 'vue';
import { MessagePropsTypes, MessageParams, messageTypes, MessageFn, Message } from './message';
import { isString, isFunction } from '../utils';
import MessageContructor from './Message.vue';
let seed = 1;
const instances: VNode[] = [];
let zIndex = 2000;
const message: MessageFn & Partial<Message> = function (
options: MessageParams = {},
context?: AppContext | null,
) {
if (isString(options) || isVNode(options)) {
options = { message: options } as MessageParams;
}
const id = `message_${seed++}`;
let offset = 20;
instances.forEach(v => {
offset += v.el?.offsetHeight + 16;
});
const userOnClose = options.onClose;
const props: Partial<MessagePropsTypes> = {
...options,
zIndex: zIndex++,
id,
offset,
onClose: () => {
close(id, userOnClose);
},
};
const messageContent = props.message;
const vm = createVNode(
MessageContructor,
props,
isFunction(messageContent)
? { default: messageContent }
: isVNode(messageContent)
? { default: () => messageContent }
: null,
);
vm.props!.onDestroy = () => {
render(null, container);
};
const container = document.createElement('div');
render(vm, container);
let appendTo: HTMLElement | null = document.body;
if (isString(options.appendTo)) {
appendTo = document.querySelector(options.appendTo);
} else if (options.appendTo && options.appendTo instanceof Element) {
appendTo = options.appendTo;
}
appendTo!.appendChild(container.firstElementChild!);
instances.push(vm);
return {
close: () =>
((vm.component!.proxy as ComponentPublicInstance<{ visible: boolean }>).visible = false),
};
};
message函数:是消息提示的全局 API,通过它我们可以在任意地方调用message来显示消息。- 动态创建组件实例:每次调用
message,都会创建一个新的Message组件实例并挂载到body上。 close方法:返回一个闭包,用来关闭相应的消息实例。
3.2 支持不同类型的消息
messageTypes.forEach(type => {
(message as any)[type] = (options: MessageParams = {}, appContext?: AppContext | null) => {
if (isString(options) || isVNode(options)) {
options = { message: options } as MessageParams;
}
message({ ...options, type }, appContext);
};
});
- messageTypes:定义了四种消息类型(success, info, warning, error)。根据不同的类型,显示不同样式的消息。
3.3 关闭所有消息实例
function closeAll() {
for (let i = instances.length - 1; i >= 0; i--) {
const instance = instances[i].component;
(instance?.proxy as any).close();
}
}
message.closeAll = closeAll;
closeAll:方法可以关闭所有正在显示的消息实例。
4. 使用示例
在 Vue 应用中,我们可以通过以下方式来使用这个消息组件:
import message from './message';
message.success('This is a success message!');
message.error('This is an error message!', { duration: 5000 });
message.info('This is an informational message!');
message.warning('This is a warning message!', { showClose: true });
你可以根据需要传递不同的配置项(如 duration、offset、showClose 等)来定制消息的显示效果。
5. 总结
这个封装的消息提示组件为你的 Vue 项目提供了灵活的消息弹出功能,支持不同类型的消息显示、自动关闭、手动关闭等功能。通过全局管理消息实例,我们避免了频繁的 DOM 操作,同时也保持了 Vue 组件的良好架构。如果需要扩展功能,可以继续优化此组件,加入更多的配置选项或动画效果。
2365





