函数式弹窗控制
在Element的ElMessage
中,可以用
ElMessage.success("success")
来控制一个弹窗的展示
其原理大概是在document.createElement()
来创建一个div,用h函数创建一个Vue弹窗组件的VNode
,然后利用render函数来将组件挂载到div中,然后再将div插入至body中
就达到了命令式(函数式)创建一个消息组件,然后渲染该组件的目的
那么在ElDialog
中就没有这么方便,需要用v-model绑定visible,来控制显示和隐藏,但是基本上没有遇到太多需要在Eldialog
外部控制显示隐藏的需求,大多都是点击一个按钮,弹窗加载,然后在弹窗内部控制显隐,所以使用类似于ElMessage
这种函数式打开一个弹窗的功能就显得尤为方便
创建一个弹窗流程如下,分为4步
import MessageDialog from './MessageDialog.vue'
import { render, h } from 'vue'
function createDialog(){
//1.创建一个div
const dom = document.createElement('div')
//2.将Dialog组件通过h函数,生成vNode虚拟dom节点
const vNode = h(MessageDialog, { message: 'hello World'})
//3.将vNode挂载到dom上
render(vNode, dom)
//4.将挂在好的dom插入进body
document.body.appendChild(dom.firstChild)
}
此时,我们挂载并渲染了MessageDialog
组件,但是并没有去控制它的显示和隐藏
我们光是挂载进去了,但是此时Dialog的visible还是false,我们此时还看不到
所以,我们组件中需要暴露出来一些函数,比如showDialog
控制Dialog显示, closeDialog
控制Dialog关闭 ,dialogVisible
查看dialog此时的显隐情况
组件如下
<script lang="ts" setup>
import { ref } from 'vue'
import { ElDialog } from 'element-plus'
const dialogVisible = ref(false)
//定义props
const props = defineProps({
message: {
type: String,
default: '这是一段信息'
}
})
const showDialog = ()=>{
dialogVisible.value = true
}
const closeDialog = ()=>{
dialogVisible.value = false
}
//定义暴露出去的函数和变量
defineExpose({
showDialog,dialogVisible,closeDialog
})
</script>
<template>
<div>
<el-dialog
title="提示"
v-model="dialogVisible">
<span>{{ message }}</span>
</el-dialog>
</div>
</template>
<style scoped>
</style>
然后我们在createDialog
函数中添加openDialog()
函数的调用
function createDialog(){
//1.创建一个div
const dom = document.createElement('div')
//2.将Dialog组件通过h函数,生成vNode虚拟dom节点
const vNode = h(MessageDialog, { message: 'hello World'})
//3.将vNode挂载到dom上
render(vNode, dom)
//4.将挂在好的dom插入进body
document.body.appendChild(dom.firstChild)
//5.让弹窗组件显示Dialog,将visible变为true
vNode.component.exposed.showDialog()
}
现在,我们每调用一次createDialog
都会新建一个弹窗并打开
我们想要在每次打开中,不新建弹窗,而是将原先的弹窗重新打开
那么此时,我们在这个method.ts
文件中,新建一个dialog
变量,这个变量的作用域为这个文件,我们在使用import导入这个文件的时候,就会产生一个闭包,在DialogMethod中保留着dialog的值
import MessageDialog from "./MessageDialog.vue"
import { render, h,type ComponentInternalInstance } from 'vue'
let dialog: ComponentInternalInstance | null = null
export const DialogMethod = ()=>{
DialogMethod.open()
}
DialogMethod['open'] = () => {
if(!dialog){
const dom = document.createElement('div')
const vNode = h(MessageDialog, { message: 'hello World', onClose: () => { console.log('close') } })
render(vNode, dom)
document.body.appendChild(dom.firstChild)
vNode.component.exposed.showDialog()
dialog = vNode.component
}
else{
dialog.exposed.showDialog()
}
}
DialogMethod['close'] = () => {
dialog.exposed.closeDialog()
}
我们还传入了一个onClose
函数,就是在DialogMethod
中,能够在这个弹窗关闭的时候执行一些回调函数
打开弹窗 DialogMethod.open()或者DialogMethod()
,关闭弹窗 DialogMethod.close()
最终代码如下
method.ts
:
import MessageDialog from "./MessageDialog.vue"
import { render, h,type ComponentInternalInstance } from 'vue'
let seed = 1
let dialog: ComponentInternalInstance | null = null
export const DialogMethod = ()=>{
console.log(seed++)
DialogMethod.open()
}
DialogMethod['open'] = () => {
if(!dialog){
const dom = document.createElement('div')
const vNode = h(MessageDialog, { message: 'hello World', onClose: () => { console.log('close') } })
render(vNode, dom)
document.body.appendChild(dom.firstChild)
console.log(vNode.component.exposed)
vNode.component.exposed.showDialog()
dialog = vNode.component
}
else{
dialog.exposed.showDialog()
}
}
DialogMethod['close'] = () => {
console.log('close')
dialog.exposed.closeDialog()
}
MessageDialog.vue
:
<script lang="ts" setup>
import { ref,watch } from 'vue'
import { ElDialog,ElButton } from 'element-plus'
const dialogVisible = ref(false)
const props = defineProps({
message: {
type: String,
default: '这是一段信息'
},
onClose:{
type: Function,
default: ()=>{}
}
})
const showDialog = ()=>{
dialogVisible.value = true
}
defineExpose({
showDialog,dialogVisible,closeDialog:()=>{
dialogVisible.value = false
}
})
const dialog = ref(null)
watch(dialogVisible,(val)=>{
if(val===false){
props.onClose()
}
})
</script>
<template>
<div>
<el-dialog ref="dialog"
title="提示"
v-model="dialogVisible">
<span>{{ message }}</span>
</el-dialog>
</div>
</template>
<style scoped>
</style>