使用函数优雅地创建 ant-design-vueview-designElementUI 的 Drawer 和 Modal

例子

vue-create-dm使用例子在线地址

代码见 example 文件夹

特性

  • 通过函数来创建ModalDrawer组件
  • ModalDrawer的内容子组件的createdmounteddestoryed生命周期按照正常逻辑触发
  • ModalDrawer支持分别注册全局头部组件(需要接收名为 titleprops
  • 支持传入 titlecontentfooter 插槽
  • 支持ModalDrawer与父组件通信
  • 支持子组件获取 this.$storethis.$router
  • 支持传入路由来匹配内容组件,
    • 若传入url比如 https://www.baidu.com ,则以 iframe形式展示
    • 若传入 相对路由(比如 /foo, /bar),则获取匹配的路由组件展示
  • 支持微前端中跨项目相互调用

为什么

在使用弹窗抽屉组件的过程中,你是否也曾遇到过以下场景:

  1. 一个项目里有许多的弹窗和抽屉类型的交互,有时甚至一个页面组件里就有许多弹窗和抽屉组件,原生的使用方式是先在父组件中写好弹框抽屉组件,然后通过visible变量来控制弹窗的显示隐藏,当弹窗抽屉一多,看着各种xxVisible让人感觉很混乱

  2. 弹窗抽屉内包含的子组件的生命周期并没有按我们预想的逻辑触发,我们想打开弹窗抽屉的时候才触发内容子组件的createdmounted生命周期,然而实际上却并不是;我们希望关闭的时候可以调用子组件的destoryed生命周期,可是目前的UI框架大多只是把组件设置为display:none了,并没有完全卸载子组件,

  3. antd提供了destroyOnClose参数支持关闭时销毁子元素,但也没法解决上面说到的1,2两点问题组件库虽然也有提供通过函数打开弹窗的方法,但那些都是一些简单的弹框,可配置的参数不多,自由度也不够高

因此就有了vue-create-dm这个库,dm就是分别取了DrawerModal的第一个字母组合在一起(为什么不是md呢,因为mdmarkdown的缩写…)目前内置支持了ant-design-vueview-designElementUI三个组件库的的弹框抽屉组件,并且提供了各种工具函数可以自己支持其他组件库的弹框抽屉组件

安装

1
yarn add vue-create-dm

统一注册

注意1
如果要在子组件内获取 this.$storethis.$router 请把 VueCreateDM 的注册放到 VuexVueRouter 实例生成之后,并且传入这两个实例

注意2
如果要自定义全局头部组件,请传入modalGlobalHeader,drawerGlobalHeader这两个参数,分别对应Modal组件的全局头部和Drawer组件的全局头部

下面演示如何进行全量注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Vue from 'vue';
import VueCreateDM from 'vue-create-dm';
import { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';
import { Modal as viewModal, Drawer as viewDrawer } from 'view-design';
import { Dialog as eleModal, Drawer as eleDrawer } from 'element-ui';
import store from './store'
import router from './router';
import modalGlobalHeader from './components/modalGlobalHeader';
import drawerGlobalHeader from './components/drawerGlobalHeader';

Vue.use(VueCreateDM, {
antdModal,
antdDrawer,
viewModal,
viewDrawer,
eleModal,
eleDrawer,
store,
router,
modalGlobalHeader,
drawerGlobalHeader,
});

单个注册

注意1
如果要在子组件内获取 this.$storethis.$router 请把 VueCreateDM 的注册放到 VuexVueRouter 实例生成之后,并且传入这两个实例

注意2
如果要自定义全局头部组件,请传入globalHeader参数

下面演示如何单个注册,其中component属性必传,其余几个都是可选参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue';
import store from './store';
import router from './router';
import { createAntdDrawer } from 'vue-create-dm';
import { Drawer } from 'ant-design-vue';
import globalHeader from '../components/globalHeader';

Vue.use(createAntdDrawer, {
component: Drawer,
router, // 子组件需要用到 this.$router 就传
store // 子组件需要用到 this.$store 就传
globalHeader, // 全局配置头部组件
});

使用

创建抽屉

1
2
3
this.$createAntdDrawer(options, arg1, arg2);
this.$createViewDrawer(options, arg1, arg2);
this.$createEleDrawer(options, arg1, arg2);

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
this.$createAntdDrawer({
drawerProps: {
title: '标题',
width: '500px',
mask: false,
},
content: {
template: HelloWorld,
props: {
msg: 'Welcome to Your Vue.js App',
},
},
beforeClose: function() {
console.log('我要关闭了');
},
afterClose: function() {
console.log('我已经关闭了');
},
});

创建弹框

1
2
3
this.$createAntdModal(options, arg1, arg2);
this.$createViewModal(options, arg1, arg2);
this.$createEleModal(options, arg1, arg2);

代码示例

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
this.$createAntdModal({
modalProps: {
title: '标题',
width: '500px',
mask: false,
},
content: {
template: HelloWorld,
props: {
msg: 'Welcome to Your Vue.js App',
},
},
beforeClose: function() {
console.log('我要关闭了');
},
afterClose: function() {
console.log('我已经关闭了');
},
async onOk() {
const res = await new Promise((resolve) => {
setTimeout(() => {
console.log('点了确定');
resolve(false);
}, 3000);
});
return res;
},
});

微前端中使用

基于single-spa, vue-create-dm也支持在微前端项目中跨项目调用抽屉、弹框

前提条件

  1. 需要互相调用抽屉、弹框的子项目必须都是 vue 技术栈
  2. 并且都安装注册了vue-create-dm
  3. 要跨项目调用的页面注册成路由页面

示例项目

参考 micro-frontend-example 文件夹

主项目使用

  1. 注册子应用的方式需要改成config配置文件的方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const config = [{
    name: 'sub1',
    app: () => loadSubApp('sub1', 'http://localhost:8081/manifest-initial.json'),
    activeWhen: (location) => location.pathname.startsWith('/sub1'),
    customProps: {
    domElement: '#app-sub-wrapper',
    },
    }]
    export default config;
  2. main.js加入如下代码

    1
    2
    3
    4
    5
    6
    import { listenOpenDrawerAction, triggerOpenDrawerAction } from 'vue-create-dm';
    import config from './micro-frontend/config/config';
    // 监听打开抽屉事件
    listenOpenDrawerAction(config, '$createAntdDrawer');
    // 子项目调用打开抽屉的函数
    window.triggerOpenDrawerAction = triggerOpenDrawerAction;

子项目使用

  1. 除了 single-spa 要求导出的生命周期方法之外,子项目的main.js需要额外导出一个router实例和一个空的vue实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import Vue from 'vue';
    import router from './router';
    import store from './store';
    import VueCreateDM from 'vue-create-dm';
    import { Modal as antdModal, Drawer as antdDrawer } from 'ant-design-vue';

    Vue.use(VueCreateDM, {
    antdDrawer,
    antdModal,
    router,
    store,
    });

    export const bootstrap = function(){...}
    export const mount = function(){...}
    export const unmount = function(){...}
    export const update = function(){...}
    // 额外导出
    export const $router = router;
    export const $Vue = new Vue();
  2. 需要被跨项目调用的组件,请在子项目的VueRouter路由配置文件中声明

  3. 子项目触发openDraweropenModal事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    window.triggerOpenDrawerAction({
    appName: 'sub2',
    path: '/about',
    drawerProps: {
    title: '子应用二抽屉',
    width: '50%',
    },
    content: {},
    });


PS: 如果觉得不错, 欢迎前往 Github vue-create-dm 点个star

友情链接: