场景
- 有一个表格
- 有一个列用的自定义组件
- 这个组件里需要打开一个弹窗
一般写法
是在每个单元格自定义组件里引用弹窗组件。
缺点
- 表格渲染会创建很多弹窗组件实例,降低性能。
新的思路
定义一个函数,在执行函数时,实时创建弹窗组件并挂载,函数返回一个promise,promise在弹窗组件点击确定的时候resolve(data),点击关闭的时候reject
消费示例,其中open-dialog.js在下面定义
import openDialog from './open-dialog.js'
// 点击打开弹窗,获取弹窗里的数据
const handleClick = async ()=>{
const data = await openDialog();
// 使用返回的data数据进行后续操作
// ……
}
优点
- 按需实例化弹窗组件,只生成一个实例,提升性能。
- 函数式调用,返回promise,promise返回数据,可以使用同步编码逻辑,无需在主组件的template中引入弹窗组件,也不用写回调事件
主要代码
主体js文件,接收options,传递options、confirm事件、close事件给创建的弹窗组件实例。返回一个promise
// open-dialog.js
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import Dialog from './dialog.vue'
const openDilaog = (options) => {
return new Promise((resolve, reject) => {
// 创建一个dom,作为挂载点
const div = document.createElement('div')
document.body.appendChild(div)
// 创建弹窗组件应用实例
let app = createApp(Dialog, {
// 作为props传递给Dialog组件
...options,
// 等价于 <Dialog @confirm="xxxx" />
onConfirm: (data) => {
// 返回弹窗组件内部要传出来的数据
resolve(data)
// 卸载应用实例
app?.unmount()
// 移除挂载点dom
document.body.removeChild(div)
// 释放应用实例占用的内存
app = null
},
// 等价于 <Dialog @close="xxxx" />
onClose: (data) => {
// 弹窗关闭
reject(data)
app?.unmount()
document.body.removeChild(div)
app = null
}
})
// vue3的应用实例都是隔离的,所以这里要单独安装element-plus插件,否则Dialog里的element组件不会渲染,如果有用到element-plus的icon组件,也同理需要安装插件,我们这里有app,参考element-plus官网文档,相信你知道怎么安装icon组件
app.use(ElementPlus, { locale: zhCn })
// 挂载应用实例,并返回组件实例
const vm = app.mount(div)
// 手动打开弹窗,如果Dialog组件内的visible默认是true,可以不需要这一步。
vm.open()
})
}
// 这么写,方便import的时候兼容各种写法
export {openDilaog, openDilaog as default}
dialog组件代码,定义open函数,confirm、close事件
<!-- dialog.vue -->
<template>
<el-dialog v-model="dialogVisible" title="Tips" width="500">
<span>This is a dialog</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm"> 确认 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
const dialogVisible = ref(false)
const emit = defineEmits(['confirm', 'close'])
const open = () => {
dialogVisible.value = true
}
const handleConfirm = () => {
emit('confirm', { data: 'confirm' })
dialogVisible.value = false
}
const handleCancel = () => {
emit('close', { data: 'close' })
dialogVisible.value = false
}
defineExpose({ open })
</script>
使用
import openDialog from './open-dialog.js'
// 点击打开弹窗,获取弹窗里的数据
const handleClick = async ()=>{
const data = await openDialog();
}