前言:为什么需要弹窗工厂?
在Vue3项目中,我们经常需要动态创建各种弹窗组件。传统的弹窗实现方式通常需要在父组件中手动管理弹窗的显示/隐藏状态,这种方式存在以下问题:
- 状态管理复杂:每个弹窗都需要单独的响应式状态
- DOM操作繁琐:需要手动处理DOM的创建和销毁
- 代码重复:相似的弹窗逻辑需要重复编写
- 耦合度高:弹窗组件与父组件紧密耦合
DialogFactory采用工厂模式,提供了一种更加优雅的弹窗解决方案,通过动态创建组件实例,实现了弹窗的解耦和复用。
对比展示:传统方式 vs DialogFactory
传统Element Plus Dialog使用方式
js
<template>
<div>
<el-button @click="dialogVisible = true">打开弹窗</el-button>
<el-dialog
v-model="dialogVisible"
title="基础弹窗"
width="500px"
>
<div>弹窗内容</div>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref } from 'vue'
const dialogVisible = ref(false)
const handleConfirm = () => {
dialogVisible.value = false
}
</script>
DialogFactory使用方式
js
<template>
<el-button @click="openDialog">打开弹窗</el-button>
</template>
<script setup>
import { DialogFactory } from '@/components/MDBaseDialog'
import MyDialog from './MyDialog.vue'
const openDialog = () => {
DialogFactory(MyDialog)().then(value => {
console.log('弹窗返回值:', value)
})
}
</script>
对比总结:
- DialogFactory将弹窗的创建和管理封装在工厂函数中
- 不需要在父组件中维护弹窗的显示状态
- 通过Promise处理弹窗的确认和取消事件
- 支持动态创建任意弹窗组件
DialogFactory实现原理
核心实现代码
javascript
// src/components/MDBaseDialog/index.js
import { createVNode, render } from 'vue'
import { app } from '@/main'
/**
* 弹窗工厂函数
* @param {VueComponent} component 弹窗组件
*/
export function DialogFactory(component) {
return (options = {}) => {
return new Promise((resolve, reject) => {
// 创建容器元素
const container = document.createElement('div')
const idName = `md-dialog-box-${+new Date()}`
container.className = 'md-dialog-box'
container.id = idName
// 关闭弹窗的处理函数
const close = () => {
setTimeout(() => {
removeDialog()
}, 300) // 等待动画结束
}
// 移除DOM元素
const removeDialog = () => {
const dom = document.getElementById(idName)
dom && document.body.removeChild(dom)
}
// 确认按钮处理
options.confirm = value => {
resolve(value)
close()
}
// 取消按钮处理
options.cancel = () => {
reject('cancel')
close()
}
// 创建组件实例
const vnode = createVNode(component, options)
vnode.appContext = app._context
// 渲染组件到DOM
render(vnode, container)
document.body.appendChild(container)
})
}
}
关键技术点解析
- 动态组件创建 :使用
createVNode创建虚拟节点 - DOM操作 :通过
render函数将组件渲染到动态创建的容器中 - Promise异步处理:使用Promise处理弹窗的确认和取消事件
- 自动清理:提供自动移除DOM的机制,避免内存泄漏
使用示例
基础弹窗组件
js
<!-- MyDialog.vue -->
<template>
<el-dialog title="基础弹窗" v-model="visible" width="500px" center>
<div>这是弹窗内容</div>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
const props = defineProps({
confirm: {
type: Function
},
cancel: {
type: Function
}
})
let visible = $ref(true)
const confirm = () => {
props.confirm('确认值')
}
const cancel = () => {
props.cancel()
}
</script>
使用DialogFactory打开弹窗
js
<template>
<el-button @click="openDialog">打开弹窗</el-button>
</template>
<script setup>
import { DialogFactory } from '@/components/MDBaseDialog'
import MyDialog from './MyDialog.vue'
const openDialog = () => {
DialogFactory(MyDialog)().then(value => {
console.log('用户点击了确定,返回值:', value)
}).catch(reason => {
console.log('用户点击了取消:', reason)
})
}
</script>
带参数的弹窗
js
<!-- ParameterDialog.vue -->
<template>
<el-dialog :title="title" v-model="visible" width="600px" center>
<div>{{ message }}</div>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
const props = defineProps({
title: String,
message: String,
confirm: Function,
cancel: Function
})
let visible = $ref(true)
</script>
js
<template>
<el-button @click="openParameterDialog">打开带参数的弹窗</el-button>
</template>
<script setup>
import { DialogFactory } from '@/components/MDBaseDialog'
import ParameterDialog from './ParameterDialog.vue'
const openParameterDialog = () => {
DialogFactory(ParameterDialog)({
title: '提示',
message: '这是一个带参数的弹窗'
}).then(value => {
console.log('弹窗确认:', value)
})
}
</script>
复杂表单弹窗
js
<!-- FormDialog.vue -->
<template>
<el-dialog title="用户信息" v-model="visible" width="600px" center>
<el-form :model="formData" label-width="80px">
<el-form-item label="姓名">
<el-input v-model="formData.name" />
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="formData.email" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">保存</el-button>
</template>
</el-dialog>
</template>
<script setup>
const props = defineProps({
confirm: Function,
cancel: Function
})
let visible = $ref(true)
const formData = reactive({
name: '',
email: ''
})
const confirm = () => {
props.confirm(formData)
}
const cancel = () => {
props.cancel()
}
</script>
js
<template>
<el-button @click="openFormDialog">打开表单弹窗</el-button>
</template>
<script setup>
import { DialogFactory } from '@/components/MDBaseDialog'
import FormDialog from './FormDialog.vue'
const openFormDialog = () => {
DialogFactory(FormDialog)().then(formData => {
console.log('表单数据:', formData)
// 处理表单数据
})
}
</script>
总结
DialogFactory通过工厂模式实现了弹窗组件的动态创建和管理,具有以下优势:
- 解耦设计:弹窗组件与父组件完全解耦,提高代码复用性
- 简化API:使用Promise处理异步操作,代码更加简洁
- 自动管理:自动处理DOM的创建和销毁,避免内存泄漏
- 灵活扩展:支持任意弹窗组件,易于扩展和维护
这种模式特别适合在大型项目中使用,能够显著提高开发效率和代码质量,是Vue3项目组件化开发的重要实践。