写弹窗类组件

68 阅读1分钟

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>