目录
一、HTML标签部分
二、Script脚本部分
1、引入所需API
2、定义接收配置项
3、定义Emit方法
三、完整子组件代码
四、父组件引用
Vue3和TypeScript前,本人开发工作中一直运用较多的技术框架就是Vue2和JavaScript了。在一次偶然的机会里接到一个基于Vue3+TypeScript的项目,才开始接触Vue3和TypeScript。于是花了差不多两周的时间去学习Vue3及TypeScript的基本操作语法,类型校验等;其中Ts的语法校验及数据类型校验的严格让我一度抓耳挠腮,就这样不断在各大技术论坛中来回游走,研究官方文档介绍。也是在两周时间的苦熬下,基本对Vue3、TypeScript的语法及开发流程有所小掌握。下面就给大家分享一个我在Vue3+TypeScript开发学习过程中使用Element Plus 二次封装的一个Dialog弹窗组件的(小呆萌)!!!!
话不多说,展示!!
一、HTML标签部分
将Dialog弹窗的常用属性用Props定义公共配置项便于复用更改
<template>
<el-dialog v-model="dialog" append-to-body :modal="props.dialogConfig.dialogModal"
:close-on-click-modal="props.dialogConfig.dialogClickModalClose"
:close-on-press-escape="props.dialogConfig.dialogESCModalClose" :draggable="props.dialogConfig.dialogDraggable"
:show-close="props.dialogConfig.dialogShowClose" :title="props.dialogConfig.dialogTitle"
:width="props.dialogConfig.dialogWidth" :align-center="props.dialogConfig.dialogAlignCenter"
:center="props.dialogConfig.dialogContentCenter || true" @open="open" @close="close">
<slot name="content">
</slot>
<template #footer v-if="props.dialogConfig.dialogFooterBtn">
<el-button type="primary" @click="SaveSubmit">保存提交</el-button>
<el-button type="info" @click="CloseSubmit">取消保存</el-button>
</template>
</el-dialog>
</template>
二、Script脚本部分
1、引入所需API
import { defineProps, withDefaults, ref, defineEmits, watch } from 'vue'
2、定义接收配置项
这里需要注意的是Porps配置项定义的方式Ts和Js是不一样的,Ts需要Vue3中特定的API函数withDefaults方法来转化defineProps,再将定义的interface对象传入其中作为转译参数,抛给父组件以便于接收父组件传来的值。当然这只是我自己琢磨所理解的!其中还有很多方式是靠自己摸索出来的哈
interface Props {
visible: boolean,
dialogConfig?: {
dialogTitle: string; //模态框标题名称
dialogWidth: string; //模态框弹窗宽度
dialogShowClose: any; //是否显示关闭按钮
dialogModal: any; //是否需要模态框(遮罩层)
dialogAlignCenter: any; //是否水平垂直对齐模态框
dialogContentCenter: any; //模态框header和footer内容是否居中对齐
dialogFullscreen: any; //模态框是否为全屏
dialogClickModalClose: any; //是否可以通过点击遮罩层关闭Dialog
dialogESCModalClose: any; //是否可以通过按下ESC关闭Dialog
dialogDraggable: any; //是否开启模态框拖拽功能
dialogFooterBtn: any; //是否开启底部操作按钮
}
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
dialogConfig: () => {
return {
dialogTitle: "默认标题" || "",
dialogWidth: "40%" || "",
dialogShowClose: "" || false,
dialogModal: true || "",
dialogAlignCenter: true || "",
dialogContentCenter: "",
dialogFullscreen: "" || false,
dialogClickModalClose: "" || false,
dialogESCModalClose: "" || false,
dialogDraggable: "" || false,
dialogFooterBtn: "" || false,
}
}
})
//定义默认Dialog弹窗打开状态
const dialog = ref<boolean>(props.visible)
// watch监听
watch(() => props.visible, (newValue, oldValue) => {
dialog.value = newValue
}, { deep: true, immediate: true })
这里我使用的是watch监听的方法来监测dialog状态的发生变化 ,从而实现弹窗的开启与关闭状态,当然目前还要最后的步骤才能完全实现弹窗的打开与关闭状态!!!
3、定义Emit方法
// 定义Emits数据类型
interface Emits {
(event: 'save', isShow: boolean): void
(event: 'cancellation', isShow: boolean): void
(event: 'open', isShow: boolean): void
(event: 'close', isShow: boolean): void
}
const emits = defineEmits<Emits>() //将定义Emits数据类型赋值
// 保存提交回调函数
const SaveSubmit = () => {
emits('save', false) //emit方法供父级组件调用
}
// 取消保存回调函数
const CloseSubmit = () => {
emits('cancellation', false) //emit方法供父级组件调用
}
// 打开事件回调函数
const open = () => {
emits('open', true) //emit方法供父级组件调用
}
// 关闭事件回调函数(当显示头部关闭按钮时需调用该回调函数方法 -> dialogShowClose = true 反之)
const close = () => {
emits('close', false) //emit方法供父级组件调用
}
这里我定义了四个事件,当中open和close方法为Dialog组件标签本身事件,可以使用另一种ref方法去控制弹窗的开启与关闭状态,使用ref方法需配合defineExpose函数API方法将open与close抛出,才可供父组件调用。这里我多封装了两个方法是项目中弹窗多为表单控件,所以定义了表单提交与取消方法的按钮组件,需要时显示,不需要时隐藏即可。这里我是为了自己方便所以才这样写,大家可根据自己的想法自行更改和数据封装。
三、完整子组件代码
<template>
<el-dialog v-model="dialog" append-to-body :modal="props.dialogConfig.dialogModal"
:close-on-click-modal="props.dialogConfig.dialogClickModalClose"
:close-on-press-escape="props.dialogConfig.dialogESCModalClose" :draggable="props.dialogConfig.dialogDraggable"
:show-close="props.dialogConfig.dialogShowClose" :title="props.dialogConfig.dialogTitle"
:width="props.dialogConfig.dialogWidth" :align-center="props.dialogConfig.dialogAlignCenter"
:center="props.dialogConfig.dialogContentCenter || true" @open="open" @close="close">
<slot name="content">
</slot>
<template #footer v-if="props.dialogConfig.dialogFooterBtn">
<el-button type="primary" @click="SaveSubmit">保存提交</el-button>
<el-button type="info" @click="CloseSubmit">取消保存</el-button>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { defineProps, withDefaults, ref, defineEmits, watch } from 'vue'
interface Props {
visible: boolean,
dialogConfig?: {
dialogTitle: string; //模态框标题名称
dialogWidth: string; //模态框弹窗宽度
dialogShowClose: any; //是否显示关闭按钮
dialogModal: any; //是否需要模态框(遮罩层)
dialogAlignCenter: any; //是否水平垂直对齐模态框
dialogContentCenter: any; //模态框header和footer内容是否居中对齐
dialogFullscreen: any; //模态框是否为全屏
dialogClickModalClose: any; //是否可以通过点击遮罩层关闭Dialog
dialogESCModalClose: any; //是否可以通过按下ESC关闭Dialog
dialogDraggable: any; //是否开启模态框拖拽功能
dialogFooterBtn: any; //是否开启底部操作按钮
}
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
dialogConfig: () => {
return {
dialogTitle: "默认标题" || "",
dialogWidth: "40%" || "",
dialogShowClose: "" || false,
dialogModal: true || "",
dialogAlignCenter: true || "",
dialogContentCenter: "",
dialogFullscreen: "" || false,
dialogClickModalClose: "" || false,
dialogESCModalClose: "" || false,
dialogDraggable: "" || false,
dialogFooterBtn: "" || false,
}
}
})
const dialog = ref<boolean>(props.visible)
// watch监听
watch(() => props.visible, (newValue, oldValue) => {
dialog.value = newValue
}, { deep: true, immediate: true })
// 定义Emits数据类型
interface Emits {
(event: 'save', isShow: boolean): void
(event: 'cancellation', isShow: boolean): void
(event: 'open', isShow: boolean): void
(event: 'close', isShow: boolean): void
}
const emits = defineEmits<Emits>() //将定义Emits数据类型赋值
// 保存提交回调函数
const SaveSubmit = () => {
emits('save', false) //emit方法供父级组件调用
}
// 取消保存回调函数
const CloseSubmit = () => {
emits('cancellation', false) //emit方法供父级组件调用
}
// 打开事件回调函数
const open = () => {
emits('open', true) //emit方法供父级组件调用
}
// 关闭事件回调函数(当显示头部关闭按钮时需调用该回调函数方法 -> dialogShowClose = true 反之)
const close = () => {
emits('close', false) //emit方法供父级组件调用
}
</script>
<style scoped lang="scss"></style>
四、父组件引用
<template>
<div id="container">
<dialog :visible="visible" :dialog-config="dialogConfig" @open="open" @close="close" />
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue'
import dialog from '@/components/Dialog.vue';
export default defineComponent({
components: {
dialog
},
setup() {
const visible = ref<Boolean>(false)//弹窗默认关闭状态
//配置项
const dialogConfig = reactive<any>({
dialogTitle: "封装的第一个Dialog弹窗组件"
})
// 打开事件回调函数
const open = (e: Boolean) => {
visible.value = e
}
// 关闭事件回调函数
const close = (e: Boolean) => {
visible.value = e
}
return {
visible,
dialogConfig,
open,
close
}
},
})
</script>
<style scoped lang="scss"></style>
这里图个方便!父组件调用弹窗组件的配置项默认我就写一个,如有更改的默认配置项可自行填写dialogConfig中对应的对象即可,父组件中不向配置项中的对象传参更改默认配置则为默认配置,具体请参考Script部分中的2、定义接收配置项。
总结:上述呢只是个人在学习开发中对运用和使用到的一些API方法和自己对组件二次封装开发的一些理解和拙见,并不是很权威。个人认为使用的方法函数和封装逻辑还是一个比较笨的方法,希望大家能在阅读这篇文章中能有自己的理解和新的封装方式,也希望那位大佬在阅读本篇文章后给出更高质量的建议和方法,大家一起学习。写得不好,献丑啦!嘿嘿。