message.ts
import { type VNode, h, render } from 'vue'
import type { MessageParams } from './props'
import MessageComponent from './Message.vue'
// 保存所有Message组件实例
const MessageList: VNode[] = []
const Message = (options: MessageParams) => {
if (typeof options == 'string') {
options = {
msg: options
}
}
/**
* 设置Message组件的offset
* 在此遍历是为了解决vm未渲染成VNode的问题,未渲染成VNode时vm.el=null
*/
let offset = 30
MessageList.forEach(vm => {
offset += vm.el!.offsetHeight + 20
})
options.offset = offset
// 创建一个Message组件容器
let container = document.createElement('div')
// 创建Message VNode,此时vm.el = null
let vm = h(MessageComponent, options)
// 渲染成VNode vm.el才有值
render(vm, container)
// 设置offset
MessageList.push(vm)
// 把Message组件插入body中
document.body.appendChild(container.firstElementChild!)
// 卸载组件
const timer = setTimeout(() => {
// 删除vm
render(null, container)
}, 3000)
}
export default Message
Message.vue
<script setup lang="ts">
interface Props {
msg: string,
type?: string,
duration?: number,
offset?: number
}
defineProps<Props>()
</script>
<template>
<div class="msg" :style="{ position: 'fixed', top: `${offset}px` }">
{{ msg }} {{ offset }}
</div>
</template>