• Vue3 函数式弹窗


    运行环境

    • vue3
    • vite
    • ts
    • element-plus

    开发与测试

    1. 使用h、render函数创建Dialog

    • 建议可在plugins目录下创建dialog文件夹,创建index.ts文件,代码如下
    import { h, render } from "vue";
    
    /**
     * 函数式弹窗
     * @param component 组件
     * @param options 组件参数
     * @returns
     */
    function createDialog(component: any, options: any) {
      return new Promise((resolve, reject) => {
        // 创建一个div节点
        const mountNode = document.createElement("div");
        // 将div节点拼接到Dom的body节点下
        document.body.appendChild(mountNode);
        // 使用h函数创建节点
        const vNode = h(component, {
          ...options,
          // 注意: vue子组件emit回调事件名称必须以on开头
          onSubmit: data => {
            resolve(data);
            // 移除节点
            document.body.removeChild(mountNode);
          },
          onCancel: data => {
            reject(data);
            // 移除节点
            document.body.removeChild(mountNode);
          }
        });
        // 渲染Dialog
        render(vNode, mountNode);
      });
    }
    
    export default createDialog;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    2. 全局挂载函数式弹窗

    • 在main.ts中引入弹窗,并挂载在app上
    // 引入函数式弹窗
    import Dialog from "@/plugins/dialog";
    
    const app = createApp(App);
    
    // 挂载到app
    app.config.globalProperties.$dialog = Dialog;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 测试

    3.1 创建一个弹窗组件 testDialog.vue
    <template>
      <el-dialog v-model="dialogVisible" title="测试函数式弹窗" width="50%">
        <span>{{ props.content }}</span>
        <template #footer>
          <span class="dialog-footer">
            <el-button @click="handleCancel">Cancel</el-button>
            <el-button type="primary" @click="handleSubmit"> Submit </el-button>
          </span>
        </template>
      </el-dialog>
    </template>
    
    <script lang="ts" setup>
    import { reactive, toRefs } from "vue";
    // 注意: 需要按需引入使用到的第三方UI组件
    import { ElDialog, ElButton } from "element-plus";
    const props = withDefaults(
      defineProps<{
        show?: boolean; // moadl开关
        content?: string; // 内容
      }>(),
      {}
    );
    const emits = defineEmits(["submit", "cancel"]);
    const state = reactive({
      dialogVisible: props.show
    });
    const { dialogVisible } = toRefs(state);
    
    /** submit */
    const handleSubmit = () => {
      // 回调
      emits("submit", { action: "submit", msg: "submit back" });
      // 关闭弹窗
      dialogVisible.value = false;
    };
    
    /** cancel */
    const handleCancel = () => {
      // 回调
      emits("cancel", { action: "cancel", msg: "cancel back" });
      // 关闭弹窗
      dialogVisible.value = false;
    };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    3.2 函数式调用弹窗
    <template>
      <!-- 动态函数式弹窗 -->
      <div class="test_dialog">
        <el-button @click="openModal">调用函数式弹窗</el-button>
      </div>
    </template>
    
    <script lang="ts" setup>
    import { getCurrentInstance } from "vue";
    import TestDialog from "./testDialog.vue";
    
    // 通过全局的上下文拿到 proxy 属性
    const { proxy } = getCurrentInstance();
    
    // 调用函数式弹窗
    const openModal = () => {
      // 调用弹窗
      proxy
        .$dialog(TestDialog, {
          show: true,
          content: "调用弹窗成功了!"
        })
        .then(res => {
          // submit
          console.log(res);
        })
        .catch(error => {
          // cancel 回调
          console.log(error);
        });
    };
    </script>
    
    <style lang="scss" scoped>
    .test_dialog {
      padding: 50px;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    3.3 测试效果

    在这里插入图片描述

    问题

    1. 非原生的html元素无法渲染,如elements-plus组件无法在弹窗渲染
      因为使用h函数无法渲染第三方UI,需要在弹窗中单独引入,如上面测试代码使用的element-plus的modal和button都需要按需引入一次。如果没有引入弹窗都不会show出来,控制台会给于警告如下截图,通过这个截图也可以看到,h函数是帮我们将弹窗组件拼接到了DOM中,组件的参数一并拼接了进去,与传统的调用方式近似。
      在这里插入图片描述

    2. 在调用dialog的代码中,ts会有代码警告
      可以全局申明下挂载的dialog,可直接在main.ts添加下面的申明

    	// 全局申明下$dialog,可以去除调用时ts的警告
    	declare module "vue" {
    	  export interface ComponentCustomProperties {
    	    $dialog: any;
    	  }
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    springboot实战(五)之sql&业务日志输出,重要
    设计模式:策略模式
    computed计算属性
    ROS学习笔记(19):建图与定位(3)
    连接池优化
    Gin框架简介和快速使用
    图像处理之图像的几何变换
    2023-11-17 服务器开发-性能分析-intel-vtune-安装
    [AIGC] 使用Google的Guava库中的Lists工具类:常见用法详解
    新版OpenCV5 (C++)版本部署目标检测:YOLOv4打包成可用AI软件
  • 原文地址:https://blog.csdn.net/qq_36330228/article/details/134483488