ElMessage 组件实现

16 篇文章 0 订阅

源码
在线预览

功能分析

参考 ElementPlus 官网给出的例子,我们的 Message 组件至少需要:

  1. 自定义各种类型的 Message(error、info、success 等)
  2. 提供静态方法调用,如 Message.success()
  3. 进入、退出动画;Message 消失后上移动画

Message 组件

样式方面 Messge 组件非常简单,就是个 fixed 居中的 div,而进入、退出动画可以通过 transition 实现,消失可以通过 props 调用回调通知调用者。

<template>
    <transition @before-leave="onClose" @after-leave="onDestroy" name="message-fade">
        <div v-show="visiable" :class="messageStyle" :style="{ top: `${top}px` }">
            <span>{{ message }}</span>
        </div>
    </transition>
</template>

<script setup>
import types from './types'
import { onMounted, ref, computed } from 'vue'
const props = defineProps({
    type: {
        type: String,
        default: 'info',
        validator(value) {
            return Object.values(types).includes(value)
        }
    },
    top: {
        type: Number,
        default: 20
    },
    message: {
        type: String,
        required: true
    },
    duration: {
        type: Number,
        default: 3000
    },
    onDestroy: Function,
    onClose: Function
})
const visiable = ref(false)
const messageStyle = computed(() => ['message', props.type])
const close = () => {
    visiable.value = false
}
onMounted(() => {
    visiable.value = true
})
setTimeout(close, props.duration)
</script>

<style scoped>
.message {
    @apply fixed z-50 left-1/2 rounded px-4 py-2 transform -translate-x-1/2 min-w-3/10 transition-all
}
.message.info {
    color: #73767a;
    background: #f4f4f5;
}
.message.danger {
    color: #c45656;
    background: #fde2e2;
}
.message.success {
    color: #529b2e;
    background: #e1f3d8;
}
.message.warning {
    color: #b88230;
    background: #faecd8;
}
.message-fade-enter-from,
.message-fade-leave-to {
    @apply transform -translate-x-1/2 -translate-y-20px opacity-0
}

显示 Message 的逻辑实现

把组件挂载到 DOM 上还是用 h + render 方式,这里主要的问题是当前一个 Message 消失后如何通知后面的 Message 上移。具体实现也不难,我们需要

  1. 创建一个数组保存所有正在显示的 Message
  2. 当 Message close 回调触发时,找到被 close 的 Message,从数组中删除,并且从该元素的下标开始依次处理后序 Message 的 top

有了 Message 实例数组后,我们还可以轻松的计算出 Message 的初始显示高度。

import { h, render } from 'vue'
import types from './types'
import MessageCpn from './Message.vue'

const instances = []

function Message(options) {
    let top = 20;

    instances.forEach(vm => {
        top += (vm.el.offsetHeight + 16) || 16
    });

    const container = document.createDocumentFragment()

    const vm = h(MessageCpn, {
        ...options,
        top,
        onClose() {
            close(vm)
        },
        onDestroy() {
            render(null, container)
        }
    })

    render(vm, container)
    document.body.appendChild(container)

    instances.push(vm)
}

function close(vm) {
    const index = instances.findIndex(ins => ins === vm)
    if (index === -1) {
        return;
    }

    instances.splice(index, 1)

    for (let start = index; start < instances.length; start++) {
        const cpn = instances[start].component
        cpn.props.top -= vm.el.offsetHeight + 16
    }
}

Object.values(types).forEach(type => {
    Message[type] = (options) => {
        options.type = type;
        return Message(options)
    }
})


export default Message
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值