仿element-ui之Message提示组件

1,730 阅读4分钟

仿element-ui之封装Message消息提示组件

前言:

大家好~ 我是小瑜,今天给大家带来的是利用Vue3的h函数封装Message消息组件,首先看一下要完成的效果.

video-2023-06-03-192941.gif

在element官网中,消息提示组件是通过引入 import { ElMessage } from 'element-plus' 实现的,那么一定是引入的某个方法, 但是一上来我们不需要考虑这么多,先把样式功能,利用组件的方法来实现目前的需求

注册组件
// components/meesage/index.vue
<script lang="ts" setup name="Message">
import { onMounted, ref } from 'vue'
export type Type = 'success' | 'error' | 'warning'
const { type = 'success' } = defineProps<{
  type?: Type,
  text: string,
}>()
// 定义一个对象,三种情况的样式,对象key就是类型字符串
const style = {
  warning: {
    icon: 'icon-warning',
    color: '#E6A23C',
    backgroundColor: 'rgb(253, 246, 236)',
    borderColor: 'rgb(250, 236, 216)'
  },
  error: {
    icon: 'icon-shanchu',
    color: '#F56C6C',
    backgroundColor: 'rgb(254, 240, 240)',
    borderColor: 'rgb(253, 226, 226)'
  },
  success: {
    icon: 'icon-queren2',
    color: '#67C23A',
    backgroundColor: 'rgb(240, 249, 235)',
    borderColor: 'rgb(225, 243, 216)'
  }
}
</script>

<template>
  <Transition name="down">
    <div class="message" :style="style[type]" v-if="isShow">
      <i class="iconfont" :class="style[type].icon"></i>
      <span class="text">{{ text }}</span>
    </div>
  </Transition>
</template>

<style scoped lang="scss">
.message {
  width: 300px;
  height: 50px;
  position: fixed;
  z-index: 9999;
  left: 50%;
  margin-left: -150px;![277c93489ae345eef3e93bd5e8cf8](D:\upupdate\demo\vue相关\vue3-demo\src\components\message\images\8277c93489ae345eef3e93bd5e8cf87.png)![277c93489ae345eef3e93bd5e8cf8](D:\upupdate\demo\vue相关\vue3-demo\src\components\message\images\8277c93489ae345eef3e93bd5e8cf87.png)
  top: 25px;
  line-height: 50px;
  padding: 0 25px;
  border: 1px solid #e4e4e4;
  background: #f5f5f5;
  color: #999;
  border-radius: 4px;
  i {
    margin-right: 4px;
    vertical-align: middle;
  }
  .text {
    vertical-align: middle;
  }
}
</style>

样式和简单的功能已经书写好了,我们去导入使用下 ,看一下效果

8277c93489ae345eef3e93bd5e8cf87.png

耶,页面上已经可以显示了,但是有两个问题需要我们进行优化

  1. 缺少动画效果

  2. 弹层无法关闭

添加动画效果

这里可以使用css去写,可以使用vue提供的Transition进行动画的使用, 可能大家都不知道如何使用, 那怎么办? 老规矩 打开vue3文档~

<Transition> 是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上....

vue3的文档写的非常详细,大家以后遇到不会的可以多去看一看,这里我就不展开,直接写代码

 <Transition name="down">
    <div class="message" :style="style[type]" v-if="isShow">
      <i class="iconfont" :class="style[type].icon"></i>
      <!-- <span class="text"><slot></slot></span> -->
      <!-- 改造一下 -->
      <span class="text">{{ text }}</span>
    </div>
 </Transition>

组件上来是从上往下淡入,并且消失的时候是从下往上淡出,基于效果,开始书写Transition动画css

// lang="scss" 这里我用到了scss 所以对重复的属性进行了简写
.down {
  &-enter {
    &-from {
      transform: translate3d(0, -75px, 0);
      opacity: 0;
    }
    &-active {
      transition: all 0.5s;
    }
    &-to {
      transform: none;
      opacity: 1;
    }
  }
  &-leave {
    &-from {
      transform: none;
      opacity: 1;
    }
    &-active {
      transition: all 0.5s;
    }
    &-to {
      transform: translate3d(0, 75px, 0);
      opacity: 0;
    }
  }
}

写完后发现,并没有出现动画, 这是什么原因? vue3有bug!

原来使用的时候必须要使用

  • v-if 所触发的切换
  • v-show 所触发的切换

基于这个特性,可以配合弹层关闭的效果

弹层自动关闭
const { type = 'success',duration=1500 } = defineProps<{
  type?: Type,
  text: string,
  duration?:number // 传入展示弹层的时间(可选,默认显示1500)
}>()

const isShow = ref(false)
// 当组件被挂载完成就显示
onMounted(()=> {
  isShow.value = true
  // 当duration时间过后,就隐藏
  window.setTimeout(() => {
    isShow.value = false
  }, duration)
})

<Transition name="down">
    <div class="message" :style="style[type]" v-if="isShow">
      <i class="iconfont" :class="style[type].icon"></i>
      <!-- <span class="text"><slot></slot></span> -->
      <!-- 改造一下 -->
      <span class="text">{{ text }}</span>
    </div>
</Transition>

此时就完了利用组件完成功能

上面说到,最终是需要通过引入方法的方式来实现,并且使用的方法是

// Message({type:'success'}, '登录成功')
// Message({type:'error'}, '登录失败')
// Message.success('登录成功')
// Message.error('登录失败')

这时候就需要使用

首先按照文档学习一下vue3中h函数的使用方法

初识h函数
import { h, render } from "vue";
// 1. 创建vnode
// 第一个参数是标签名称或者是组件名称
// 第二个参数是属性
// 第三个参数是显示内容
const vnode = h('h1', { class: 'title' }, 'hello world')
// 2. render渲染 
// 第一个参数是需要渲染的内容
// 第二个参数是挂载到哪个地方
render(vnode,document.getElementById('app'))

此时页面中就可以出现h1标签内容为hello world

9389d344d21e9def282cc7b1cc84f5b.png

好, 有了h函数以及render的知识铺垫,我们接着写message组件

利用h函数改造message组件

首先我们在对应的components下的messaga文件夹下创建index.ts文件

import { h, render } from "vue"
import MyMessage from "@/components/message/message.vue"
// 往body中系只能一个盒子,目的是同时只能出现一次消息弹层
const divContainer = document.createElement('div') as HTMLDivElement
divContainer.classList.add('message-container')
document.body.appendChild(divContainer)
// 按照基本写法的方式书写h函数
// 1. 创建vnode
const vnode = h(MyMessage,{type,text})
render(vnode,divContainer)

此时就完成了基本的功能,但是我们最终要导入方法并 Message({type:'success'}, '登录成功')来使用,也就是调用函数的方法使用

// 使用函数
type Params = {
 type: 'success' | 'error' | 'warning',
 text: string,
 duration?: number
}
function Message({ type, text, duration = 2000 }: Params) {
 // 如果要像elui一样点击就创建 并且多次点击创建多个可以帮创建放在里面
 // 往body中新增一个盒子 =>为了代码中
 const divContainer = document.createElement('div') as HTMLDivElement
 divContainer.classList.add('message-container')
 document.body.appendChild(divContainer)

 // 1. 创建虚拟dom
 // const vNode = h(MyMessage, { type, text })
 const vNode = h(MyMessage, { type, text })
 // 2. 动态render
 render(vNode, divContainer)
 clearTimeout(timer)
 // 3. 开启一个延时器,到时间就隐藏
 timer = setTimeout(() => {
  render(null, divContainer)
  }, duration)
}
优化message组件的使用

实际开发中,我们更希望利用

Message.success('成功') Message.error('失败') Message.warning('警告') 来简化代码

这就相当于帮我们再次封装了一层函数的调用

// components/message/index.ts
Message.success = (text: string, duration?: number) => {
 Message({
  text,
  type: 'success',
  duration
 })
}
Message.error = (text: string, duration?: number) => {
 Message({
  text,
  type: 'error',
  duration
 })
}
Message.warning = (text: string, duration?: number) => {
 Message({
  text,
  type: 'warning',
  duration
 })
}

在项目中的使用

// 点击按钮 出现message弹层
const showSuccess = () => {
  Message.success('成功')
}
const showError = () => {
  Message.error('失败')
}
const showWarning = () => {
  Message.warning('警告')
}
</script>

<template>
  <button @click="showSuccess">success</button>
  <button @click="showError">error</button>
  <button @click="showWarning">warning</button>
</template>
完结:

此时我们就完成了message组件的封装, 在没做之前感觉很难,但是实际动手后,其实也是非常简单的, 重在实践, 除此之外,利用h函数也可以封装dialog弹层,原理也是一样的,大家可以动手写一下~

好啦, 以上就是全部内容, 各位大佬, 可以给小瑜点个赞哦~ 手动撒花★,°:.☆( ̄▽ ̄)/$:.°★