Vue3中函数式的弹窗Dialog控制

函数式弹窗控制

在这里插入图片描述

在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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值