vue3+ts封装一个messagebox

1,209 阅读2分钟

了解vue3

Vue3.0 于 2022 年 2 月 7 日星期一成为新的默认版本!vue3的性能高,打包体积小, 由传统的options选项式 => composition组合式,大大的提高了代码的可阅读性、逻辑复用性以及更好的类型推导(全面拥抱ts),重写了diff算法(更快 更小)。

回到正题,开发中有可能因为有一些业务需求不同,很多时候不能直接使用一些组件库给我提供的组件,如一些messsage提示信息、messagebox信息弹窗等,而自己封装一个合适的组件会对开发带来很多便利。

封装组件的先了解messagebox的组件的意义以及它的作用,像封装messagebox这些组件,为了便利使用,一般封装成函数式组件。通过vue给我们提供的render渲染函数以及h函数 生成dom。

功能分析:messagebox一般有 确认/提交取消 按钮,我们需要给使用者可以使用点击/提交后执行一个回调函数,让使用者在该回调里执行一些自定义逻辑,给使用者点击取消后,执行另一个回调,所以需要用到Promise实现,思路,内部返回一个Promise对象,当使用者点击了确认按钮,执行Promise的resolve,点击取消执行Promise的reject,使用者可以使用该函数可以在它的then的第一个参数的回调执行点击确认后的自定义逻辑,then的第二个参数的回调执行取消后的操作。

如下图:

  MessageBox({
    text: '亲,您是否确认删除商品吗'
  }).then(
    () => {
      // 点击确认后干些什么...
    },
    () => {
      // 点击取消后干些什么...
    }
  )

开始动手实现

src/components下创建XZMessageBox文件夹(包括 index.ts、XZMessageBox.vue)

src/components/XZMessageBox/XZMessageBox.vue

<template>
  <div class="xz-confirm" :class="{ fade: show }">
    <div class="wrapper" :class="{ fade: show }">
      <div class="head">
        <p>{{ title }}</p>
        <i class="iconfont icon-guanbi" @click="cancelCallback"></i>
      </div>
      <div class="body">
        <i class="iconfont icon-warning"></i>
        <span>{{ text }}</span>
      </div>
      <div class="foot">
        <span class="left" @click="cancelCallback">取消</span>
        <span class="right" @click="confirmCallback">确认</span>
      </div>
    </div>
  </div>
</template>

<script setup name="XZMessageBox" lang="ts">
import { defineProps, onMounted, PropType, ref } from 'vue'

const props = defineProps({
  text: {
    type: String,
    default: '这是一条默认文本'
  },
  title: {
    type: String,
    default: '温馨提示'
  },
  点击 确认/提交 后执行 
  confirmCallback: {
    type: Function as PropType<() => void>
  },
   点击 取消 后执行 
  cancelCallback: {
    type: Function as PropType<() => void>
  }
})

const show = ref(false)

实现渐入过渡效果
onMounted(() => {
  setTimeout(() => {
    show.value = true
  }, 20)
})

</script>

<style lang="scss">
$XZColor: #27ba9b;

.xz-confirm {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 8888;
  background: rgba(0, 0, 0, 0);
  &.fade {                 // 过渡效果
    transition: all 0.4s; 
    background: rgba(0, 0, 0, 0.5);
  }
}
.wrapper {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 420px;
  height: 164px;
  padding: 15px;
  border-radius: 4px;
  background: #fff;
  opacity: 0; 
  &.fade {                  // 过渡效果
    transition: all 0.4s;
    transform: translate(-60%, -50%);    // 由右往左渐入效果
    opacity: 1;
  }
  > .head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    > .iconfont {
      cursor: pointer;
    }
    > p {
      font-size: 18px;
    }
  }
  > .body {
    margin-left: 30px;
    .icon-warning {
      margin-right: 6px;
      color: #cf4444;
    }
  }
  .foot {
    display: flex;
    justify-content: flex-end;
    > span {
      display: block;
      padding: 6px 15px;
      color: #fff;
      border-radius: 4px;
      cursor: pointer;
      &.left {
        border-color: #ccc;
        background: #ccc;
      }
      &.right {
        margin-left: 15px;
        border-color: $XZColor;
        background: $XZColor;
      }
    }
  }
}
</style>

src/components/MeMessageBox/index.ts

import { h, render } from 'vue'
import XZMessageBox from './XZMessageBox.vue'
import XZMessage from '../XZMessage'

// 定义类型
interface Props {
  text?: string
  title?: string
}

export default function MessageBox ({ text, title }: Props) {
  const div = document.createElement('div')

  div.setAttribute('class', 'xz-message-box-container')

  document.body.appendChild(div)
  
  return new Promise((resolve, reject) => {
    const confirmCallback = () => {
      document.body.removeChild(div)    // 手动移除render生成的dom
      resolve(undefined)
      XZMessage.success('删除成功!')
    }

    const cancelCallback = () => {
      document.body.removeChild(div)     // 手动移除render生成的dom
      reject(undefined)                 
      XZMessage.warning('取消成功')
    }
    const vNode = h(XZMessageBox, {
      text,
      title,
      confirmCallback,
      cancelCallback
    })
    render(vNode, div)
  })
}

使用:

  MessageBox({
    text: '亲,您是否确认删除商品吗'
  }).then(
    () => {
      // 发请求删除商品...
    },
    () => {
      // 提示信息...
    }
  )

效果图:

messagebox.png

点击确认按钮:

confirm.jpg

点击取消按钮:

cancel.jpg