欢迎您访问新疆栾骏商贸有限公司,公司主营电子五金轴承产品批发业务!
全国咨询热线: 400-8878-609

新闻资讯

常见问题

vue3动态加载对话框的方法实例

作者:用户投稿2026-01-11 07:49:15
目录
  • 简介
  • 常规方式使用对话框
  • 异步动态加载
    • 使用方式
    • TestModal.vue
    • 使用结果
  • 动态操作对话框的实现
    • DzModalService.ts
    • main.ts
  • 总结

    简介

    介绍使用vue3的异步组件动态管理对话框组件,简化对话框组件使用方式。本文使用的是vue3、typescript、element_plus完成的示例。

    常规方式使用对话框

    一般情况下,使用对话框组件,会使用v-model进行双向绑定,通过visible变量控制对话框的显示和关闭。常规方式有一个弊端,自定义组件中使用<el-dialog>,需要通过父组件控制自定义组件是否展示。

    <template>
      <ElButton type="primary" @click="openGeneral()">常规方式打开Modal</ElButton>
      <el-dialog
                 v-model="dialogVisible"
                 title="Tips"
                 width="30%"
                 :before-close="handleClose"
                 >
        <span>This is a message</span>
        <template #footer>
          <span class="dialog-footer">
            <el-button @click="dialogVisible = false">Cancel</el-button>
            <el-button type="primary" @click="dialogVisible = false"
                       >Confirm</el-button
              >
          </span>
        </template>
      </el-dialog>
    </template>
    <script setup lang="ts">
    // This starter template is using Vue 3 <script setup> SFCs
    // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
    
    import {ref } from 'vue';
    const dialogVisible = ref(false)
    
    const openGeneral=()=>{
      dialogVisible.value=true
    }
    
    </script>

    异步动态加载

    先看使用异步组件进行动态加载对话框的方式。异步组件使用到的是defineAsyncComponent接口,只有使用到这个组件,才会从网络上加载。动态操作使用的useDzModal

    使用方式

    <template>
      <ElButton type="primary" @click="openTestModalAsync()">动态异步打开TestModal</ElButton>
    </template>
    <script setup lang="ts">
    import { defineAsyncComponent,ref } from 'vue';  
    import { ElMessageBox } from 'element-plus';
    import { useDzModal } from './dzmodal'
    
    // 异步加载组件
    const TestModalAsync = defineAsyncComponent(()=>import('./components/TestModal.vue'))
    
    const dzmodal = useDzModal()
    
    // # 通过dzmodal动态操作对话框
    const openTestModalAsync=()=>{
      dzmodal.open(TestModalAsync,{
        name:'张三'
      })
        .then(res=>{
        if(res.type==='ok'){
          ElMessageBox.alert('TestModal点击了确定');
        }else{
          ElMessageBox.alert('TestModal点击了取消');
        }
      })
    }
    
    </script>

    TestModal.vue

    <script setup lang="ts">
      import { reactive, ref, defineProps } from 'vue'
    const emits = defineEmits(['ok','cancel'])
    const props = defineProps({
      name: String
    });
    const dialogVisible = ref(true)
    
    const resultData= reactive({
      type:'ok',
      data:{}
    })
    
    </script>
    
    <template>
          <el-dialog
    v-model="dialogVisible"
    title="TestModal"
    width="30%"
    >
      <div>通过DzModal打开TestModal</div>
    <div>外部传入:{{ name }}</div>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="emits('cancel',{});dialogVisible = false">Cancel</el-button>
    <el-button type="primary" @click="emits('ok',{});dialogVisible = false"
    >Confirm</el-button
    >
      </span>
    </template>
    </el-dialog>
    </template>
    
    <style scoped>
      </style>

    使用结果

    动态操作对话框的实现

    动态操作对话框,主要思路是动态创建虚拟dom节点,将对话框组件渲染到组件上,核心关键点是要动态创建的节点的上下文为当前app上下文vm.appContext = this._app._context

    DzModalService.ts

    import { App, inject, Plugin, h, render} from 'vue'
    import { ComponentOptions } from 'vue';
    export const DzModalSymbol =  Symbol()
    export class DzModalResult{
      type: 'ok'|'cancel'|'string' = 'ok'
      body?:any= undefined
    }
    
    export class DzModalService{
        
      private _app?:App=undefined
      constructor(app:App){
          this._app=app;
      }
      public open(modal:ComponentOptions, props?: any):Promise<DzModalResult>{
    
        return new Promise((reslove,reject)=>{
          if(!this._app){
            reject('_app is undefined')
            return;
          }
          
          const container = document.createElement("div");
          document.body.appendChild(container)
          
          // 这里需要合并props,传入到组件modal
          const vm = h(modal, {
            ...props,
            onOk:(data?:any)=>{
              // 弹出框关闭时移除节点
              document.body.removeChild(container)
              reslove(this.ok(data));
            },
            onCancel:(data?:any)=>{
              reslove(this.cancel(data));
            }
          });
          // 这里很重要,关联app上下文
          vm.appContext = this._app._context
          render(vm,container);
        });
      }
    
        
      public ok(data?:any):DzModalResult{
        const result = new DzModalResult();
        result.type='ok';
        result.body=data;
        return result;
      }
    
      public cancel(data?:any):DzModalResult{
        const result = new DzModalResult();
        result.type='cancel';
        result.body=data;
        return result;
      }
    }
    
    export function useDzModal(): DzModalService {
        const dzModal = inject<DzModalService>(DzModalSymbol)
        if(!dzModal){
            throw new Error('No DzModal provided!')
        }
        return dzModal;
    }
    
    const plugin: Plugin = {
        install(app:App, options?:{[key:string]:any}){
            const dzModal = new DzModalService(app)
            app.config.globalProperties.$dzModal= dzModal
            app.provide(DzModalSymbol, dzModal)
        }
    }
    
    export default plugin;

    main.ts

    import { createApp } from 'vue'
    import App from './App.vue'
    import ElementPlus from 'element-plus'
    import 'element-plus/dist/index.css'
    import DzModal from './dzmodal'
    createApp(App)
    .use(ElementPlus)
    .use(DzModal) // 安装 dzmodal插件
    .mount('#app')

    总结

    使用异步动态加载对话框,父组件无需控制对话框组件的visible属性 , 这样可以简化父组件操作,不在关心对话框组件在什么时间关闭,如果对话框组件需要访问网络,也在子组件中完成。父组件主要做两件事:

    • 通过异步组件方式引入对话框组件
    • 打开对话框组件