Vue3 如何优雅的使用 createApp 自定义通用Dialog

最近在做一个项目的技术栈升级,从Vue2升级至Vue3,Vue2中有一个通用的全局 Dialog 方法,是通过 Vue.extend 来实现的,具体请看下方的Vue2代码:

一、main.js 中定义通用方法
Vue.prototype.$dialog = {
    open(component, args) {
        return new Promise((resolve, reject) => {
            let Dialog = Vue.extend(component);
            var $vm = new Dialog({
                el: document.createElement("div"),
                router,
                store,
                eventBus: new Vue(),
            });
            var node = document.body.appendChild($vm.$el);

            $vm.open(args).then(
                result => {
                    if (resolve) {
                        resolve(result);
                    }
                    node.remove();
                    $vm.$destroy();
                },
                (arg) => {
                    if (reject) {
                        reject(arg)
                    }
                    node.remove();
                    $vm.$destroy();
                }
            );
        });
    }
};
二、定义通用 DialogLayout.vue
<template>
  <el-dialog :title="title" :visible.sync="visible" :center="center" :modal="true" :width="width"
    class="n-dialog-layout" :class="$slots.footer ? 'has-footer' : ''" :modal-append-to-body="true"
    :append-to-body="true" :lock-scroll="true" :show-close="showClose" :close-on-click-modal="false"
    :before-close="beforeClose" @opened="$emit('opened')" @close="handleClose" :fullscreen="fullscreen">
    <slot name="title" slot="title"></slot>
    <slot></slot>
    <slot name="footer" slot="footer"></slot>
  </el-dialog>
</template>
<script>
export default {
  name: "n-dialog-layout",
  props: {
    title: {},
    fullscreen: {
      default: false,
      type: Boolean,
    },
    width: {
      default: "50%",
      type: String,
    },
    showClose: {
      default: true,
      type: Boolean,
    },
    center: {
      default: false,
      type: Boolean,
    },
    beforeClose: {
      default: (done) => {
        done();
      },
      type: Function,
    },
  },
  data() {
    return {
      promise: null,
      resolve: null,
      reject: null,
      visible: false,
      confirmClose: false,
      result: {},
    };
  },
  methods: {
    open() {
      this.confirmClose = false;
      this.promise = new Promise((resolve, reject) => {
        this.resolve = resolve;
        this.reject = reject;
        this.visible = true;
      });
      return this.promise;
    },
    close(result) {
      this.confirmClose = true;
      this.result = result;
      this.visible = false;
    },
    cancel(arg) {
      this.confirmClose = false;
      this.result = arg;
      this.visible = false;
    },
    handleClose() {
      if (this.confirmClose) {
        this.resolve(this.result);
      } else {
        this.reject(this.result);
      }
    },
  },
};
</script>
三、 定义需要通过 Dialog 打开的具体页面
<template>
  <n-dialog-layout :title='l("ChangePassword")' ref="dialog">
    <div class="info" v-loading="loading">
      <el-form ref="passwordForm" status-icon size="large" :model="item" label-width="100px" label-position="top"
        class="m-b" :rules="rules">
        <el-form-item :label="l('CurrentPassword')" prop="currentPassword">
          <el-input type="password" v-model="item.currentPassword"></el-input>
        </el-form-item>
        <el-form-item :label="l('NewPassword')" prop="password">
          <el-input type="password" v-model="item.password"></el-input>
        </el-form-item>
        <el-form-item :label="l('NewPasswordRepeat')" prop="confirmPassword">
          <el-input type="password" v-model="item.confirmPassword"></el-input>
        </el-form-item>
      </el-form>
    </div>
    <template slot="footer">
      <span class="dialog-footer">
        <el-button @click="cancel()" size="large">{{ l('Cancel') }}</el-button>
        <el-button type="primary" @click="ok()" size="large">{{ l('Save') }}</el-button>
      </span>
    </template>
  </n-dialog-layout>
</template>
四、具体使用
import ChangePasswordDialog from './dialog/changePassword';
this.$dialog.open(ChangePasswordDialog).then(res => {
	this.save();
})
五、如何用 Vue3 的语法来重写 main.js 中的 $dialog 方法?
  1. app.config.globalProperties 代替 Vue.prototype;
  2. 用什么来代替 Vue.extend 呢?这里使用的 createApp;
  3. createApp 代替 Vue.extend 以后遇到的问题,例如:无法使用 ElementPlus 的UI控件、无法解析全局注册的组件
createdApp 代替 Vue.extend 实现创建一个“子类”,实现同样的效果,先看代码

问题1:无法使用 ElementPlus 的UI控件、无法解析全局注册的组件
回答: 使用 createApp 创建出来的应用实例,use ElementPlus,register 里面是我放的全局通用方法和组件
问题2:为什么Dialog.mount 的节点是写死的?而不是 动态 document.createElement ?
回答:实践过程中发现 document.createElement 通过 proxy.$dialog.open(ChangePasswordDialog) 打开正常,但是加上 .then() 就会出现关闭两次才可以正常关闭的情况

app.config.globalProperties.$dialog = {
  open(component, args) {
    return new Promise((resolve, reject) => {
      const Dialog = createApp(component);
      Dialog.use(ElementPlus);
      Dialog.use(register);
      const $vm = Dialog.mount("#Dialog");
      const node = document.body.appendChild($vm.$el);
      $vm.open(args).then(
        (result) => {
          if (resolve) {
            resolve(result);
          }
          node.remove();
        },
        (arg) => {
          if (reject) {
            reject(arg);
          }
          node.remove();
        }
      );
    });
  },
};
具体效果如下

比较灵活,可插拔的通用Dialog
在这里插入图片描述

  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Vue3 Form-Create-Designer是一个基于Vue3的表单设计器,支持自定义组件。如果你想要添加自定义组件,可以按照以下步骤进行操作: 1. 创建自定义组件 首先需要创建一个Vue3组件,可以通过Vue CLI等工具进行创建。在组件内部实现自己的功能和样式,并在最后通过export default导出该组件。 例如,我们创建了一个名为MyInput的组件: ```vue <template> <div> <input v-model="value" :placeholder="placeholder" /> </div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ props: { value: { type: String, default: '' }, placeholder: { type: String, default: '请输入' } } }) </script> <style scoped> div { border: 1px solid #ccc; padding: 10px; } input { width: 100%; height: 32px; border: none; border-bottom: 1px solid #ccc; } </style> ``` 2. 注册自定义组件 在使用Form-Create-Designer的页面中,需要通过Vue3的全局组件注册方式来注册自定义组件。在代码中引入MyInput组件,并使用Vue3的component方法进行注册。 例如: ```vue <template> <div> <form-create-designer v-model="form" :schema="schema" /> </div> </template> <script> import { defineComponent } from 'vue' import FormCreateDesigner from 'vue3-form-create-designer' import MyInput from './MyInput.vue' export default defineComponent({ components: { FormCreateDesigner, MyInput }, data() { return { form: {}, schema: { fields: [ { type: 'MyInput', model: 'input', label: '自定义输入框' } ] } } } }) </script> ``` 在上面的代码中,我们将MyInput组件注册为全局组件,并在schema中使用了type为MyInput的自定义组件。 3. 使用自定义组件 在注册好自定义组件后,就可以在Schema中使用自定义组件了。可以在Schema中使用type属性指定该字段的类型为我们刚刚注册的自定义组件。 例如: ```js { type: 'MyInput', model: 'input', label: '自定义输入框' } ``` 这样就可以在Form-Create-Designer中使用自定义组件了。 希望这个回答对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Iam_楠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值