Jansiel Notes

如何封装更简单易用的命令式组件?

什么是组件?

在vue中,组件是可复用的Vue实例,用于封装和组合可重用的UI元素。组件可以包含模板、数据、方法和样式等,它们可以独立地管理自己的状态和行为,并可以在应用程序中多次使用。

一般组件的封装及使用

组件封装了自己的模板、数据和方法,可以隐藏实现细节,提供清晰的接口供其他组件使用。这样可以降低组件之间的耦合度,提高代码的可读性。

来看下一般的组件封装,以下用消息提示组件为例:

  1. 其中script部分,接收参数,向外暴露出方法。
 1<script setup lang="ts">
 2import type { PropType } from 'vue'
 3
 4const emit = defineEmits(['cancel', 'confirm'])
 5interface IConfig {
 6  title: string
 7  message: string
 8  onConfirm?: Function
 9  onCancel?: Function
10}
11defineProps({
12  config: {
13    type: Object as PropType<IConfig>,
14    default: () => {
15      return {
16        title: '',
17        message: ''
18      }
19    }
20  }
21})
22</script>
23
  1. html和css部分,使用参数并显示,以及样式的书写。
 1<template>
 2  <view class="flex items-center justify-center absolute top-0 left-0 w-full h-full z-99999 bg-[#000000cc]">
 3    <view class="w-4/5 h-[400rpx] bg-white rounded-lg p-3">
 4      <view class="h-[260rpx] text-center">
 5        <view class="font-semibold">{{ config.title }}</view>
 6        <view>{{ config.message }}</view>
 7      </view>
 8      <view class="flex justify-between h-[100rpx]">
 9        <button class="w-1/2 mr-1" @click="emit('cancel')">取消</button>
10        <button class="w-1/2 bg-[#2979ff] text-white" @click="emit('confirm')">确认</button>
11      </view>
12    </view>
13  </view>
14</template>
15

如何使用呢?

  1. 引入组件并使用。
1import MessageBox from '@/components/MessageBox/index.vue'
2
 1<template>
 2  <view class="flex items-center w-full h-[100vh]">
 3    <view class="w-4/5 my-2 mx-auto">
 4      <u-button @click="handleOpenMsg1" type="primary">一般组件</u-button>
 5    </view>
 6    <view class="w-4/5 my-2 mx-auto">
 7      <u-button @click="handleOpenMsg2" type="primary">命令式组件</u-button>
 8    </view>
 9
10    <MessageBox v-if="isShow" :config="config" @cancel="isShow = false" @confirm="handleConfirm" />
11  </view>
12</template>
13
  1. 定义变量及方法控制
 1// 显示及配置
 2const isShow = ref<boolean>(false)
 3const config = ref({
 4  title: '一般组件',
 5  message: '这是内容'
 6})
 7
 8// 点击显示
 9const handleOpenMsg1 = () => {
10  isShow.value = true
11}
12
13// 点击确认
14const handleConfirm = () => {
15  console.log('确认');
16  isShow.value = false
17}
18

image.png

总结:

通过以上我们可以看到,组件的封装并不麻烦,只需定义好模版接收参数等等就行了,但是一般在项目中组件通用性的居多,可能在一处使用还行,如果多处使用到同一种组件的使用,会不会发现组件在使用时较为复杂,需要引入组件,定义一大堆的变量和方法,给后续的使用维护增加了较大的负担。

如何改变?

像我们经常使用的elementUI这种框架一样,在使用消息提示框的时候直接这样写就很省事:

1import { ElMessage } from 'element-plus'
2
3ElMessage({
4    type: 'info',
5    message: '消息内容',
6})
7

如何封装这样简单易用的组件呢?

还是以上面的消息提示为例,组件部分可以不用改变,在组件同级目录建一个ts文件:

image.png

需引入 createApp 将组件挂载到body中,在确认按钮或取消按钮执行后调用组件的移除。

取消事件可以不必传,默认点击关闭弹窗。当然这只是以消息组件作为示例,其他的组件封装也要考虑全面,使用起来方便即可。

 1import MessageBox from './index.vue' // 引入组件
 2import { createApp } from 'vue'
 3
 4interface IConfig {
 5  title: string
 6  message: string
 7  onConfirm?: Function
 8  onCancel?: Function
 9}
10
11// 抛出使用方法
12export default function showMsg(config: IConfig) {
13  const app = createApp(MessageBox, {
14    config, // 组件参数配置
15    onConfirm() { // 确认按钮事件
16      config.onConfirm && config.onConfirm(() => {
17        app.unmount()
18        div.remove()
19      })
20    },
21    onCancel() { // 取消按钮事件
22      if (config.onCancel) {
23        config.onCancel(() => {
24          app.unmount()
25          div.remove()
26        })
27      } else {
28        app.unmount()
29        div.remove()
30      }
31    }
32  })
33  const div = document.createElement('div')
34  document.body.appendChild(div)
35  app.mount(div)
36}
37

如何使用

引入抛出的方法:

1import showMsg from '@/components/MessageBox';
2

点击按钮时调用即可:

 1const handleOpenMsg2 = () => {
 2  showMsg({
 3    title: '命令式组件',
 4    message: '测试内容',
 5    onConfirm: (close: any) => {
 6      console.log(11111);
 7      close()
 8    },
 9    onCancel: (close: any) => {
10      console.log(22222);
11      close()
12    }
13  })
14}
15

image.png

以上就是命令式组件的封装了。当然这也不是更简洁的封装,这里将组件的vue文件和ts文件分开了;更为一体化的封装则是将vue模版组件的内容通过JSX写入到ts文件中,和react中的写法类似,这里就不过多赘述了,感兴趣的前端小伙伴们可以去尝试一下哦!