vue封装dialog_详解如何在vue+element-ui的项⽬中封装
dialog组件
1、问题起源
由于 Vue 基于组件化的设计,得益于这个思想,我们在 Vue 的项⽬中可以通过封装组件提⾼代码的复⽤性。根据我⽬前的使⽤⼼得,知道Vue 拆分组件⾄少有两个优点:
1、代码复⽤。
2、代码拆分
在基于 element-ui 开发的项⽬中,可能我们要写出⼀个类似的调度弹窗功能,很容易编写出以下代码:
我是中国地图的弹窗
我是美国地图的弹窗
我是英国地图的弹窗
打开中国地图
打开美国地图
打开英国地图
export default {
name: "View",
data() {
return {
// 对百度地图和⾕歌地图的⼀些业务处理代码 省略
cnMapVisible: false,
usaMapVisible: false,
ukMapVisible: false,
};
},
methods: {
// 对百度地图和⾕歌地图的⼀些业务处理代码 省略
openChina() {},
openUSA() {},
openUK() {},
},
};
上述代码存在的问题⾮常多,⾸先当我们的弹窗越来越多的时候,我们会发现此时需要定义越来越多的变量去控制这个弹窗的显⽰或者隐藏。
由于当我们的弹窗的内部还有业务逻辑需要处理,那么此时会有相当多的业务处理代码夹杂在⼀起(⽐如我调⽤中国地图我需要⽤⾼德地图或者百度地图,⽽调⽤美国、英国地图我只能⽤⾕歌地图,这会
使得两套业务逻辑分别位于⼀个⽂件,严重加⼤了业务的耦合度)
我们按照分离业务,降低耦合度的原则,将代码按以下思路进⾏拆分:
1、View.vue
打开中国地图
打开美国地图
打开英国地图
export default {
name: "View",
data() {
return {
/**
将地图的业务全部抽离到对应的dialog⾥⾯去,View只存放调度业务代码
*/
};
},
methods: {
openChina() {
this.$refs.china && this.$refs.china.openDialog();
},
openUSA() {
this.$refs.usa && this.$refs.usa.openDialog();
},
openUK() {
this.$refs.uk && this.$refs.uk.openDialog();
},
},
};
2、ChinaMapDialog.vue
我是中国地图的弹窗
export default {
name: "ChinaMapDialog",
data() {
return {
/
/ 对中国地图业务逻辑的封装处理 省略
baiduMapVisible: false,
};
},
methods: {
// 对百度地图和⾕歌地图的⼀些业务处理代码 省略
openDialog() {
this.baiduMapVisible = true;
},
closeDialog() {
this.baiduMapVisible = false;
},
},
};
3、由于此处仅仅展⽰伪代码,且和 ChinaMapDialog.vue 表达的含义⼀致, 为避免篇幅过长 USAMapDialog.vue 和
UKMapDialog.vue 已省略
2、问题分析
我们通过对这⼏个弹窗的分析,对刚才的设计进⾏抽象发现,这⾥⾯都有⼀个共同的部分,那就是我们对 dialog 的操作代码都是可以重⽤的代码,如果我们能够编写出⼀个抽象的弹窗,
然后在恰当的时候将其和业务代码进⾏组合,就可以实现 1+1=2 的效果。
3、设计
由于 Vue 在不改变默认的 mixin 原则(默认也最好不要改变,可能会给后来的维护⼈员带来困惑)的情
况下,如果在混⼊过程中发⽣了命名冲突,默认会将⽅法合并(数据对象在内部会进⾏递归合并,并在发⽣冲突时以组件数据优先),因此,mixin ⽆法改写本来的实现,⽽我们期望的是,⽗类提供⼀个⽐较抽象的实现,⼦类继承⽗类,若⼦类需要改表这个⾏为,⼦类可以重写⽗类的⽅法(多态的⼀种实现)。
因此我们决定使⽤ vue-class-component 这个库,以类的形式来编写这个抽象弹窗。
import Vue from "vue";
import Component from "vue-class-component";
@Component({
name: "AbstractDialog",
})
export default class AbstractDialog extends Vue {}
3.1 事件处理
查看 Element-UI 的官⽅⽹站,我们发现 ElDialog 对外抛出 4 个事件,因此,我们需要预先接管这 4 个事件。
因此需要在我们的抽象弹窗⾥预设这个 4 个事件的 handler(因为对于组件的⾏为的划分,⽽对于弹窗的处理本来就应该从属于弹窗本⾝,因此我并没有通过$listeners 去穿透外部调⽤时的监听⽅法)
import Vue from "vue";
import Component from "vue-class-component";
@Component({
name: "AbstractDialog",
})
export default class AbstractDialog extends Vue {
open() {
console.log("弹窗打开,我啥也不做");
}
close() {
console.log("弹窗关闭,我啥也不做");
}
opened() {
console.log("弹窗打开,我啥也不做");
}
closed() {
console.log("弹窗关闭,我啥也不做");
}
}
3.2 属性处理
dialog 有很多属性,默认我们只需要关注的是 before-close 和 title 两者,因为这两个属性从职责上划分是从属于弹窗本⾝的⾏为,所以我们会在抽象弹窗⾥⾯处理开关和 title 的任务
import Vue from "vue";
import Component from "vue-class-component";
@Component({
name: "AbstractDialog",
})
export default class AbstractDialog extends Vue {
visible = false;
t = "";
loading = false;
//定义这个属性的⽬的是为了实现既可以外界通过传⼊属性改变dialog的属性,也⽀持组件内部预设dialog的属性
attrs = {};
get title() {
return this.t;
}
setTitle(title) {
this.t = title;
}
}
3.3 slots 的处理
查看 Element-UI 的官⽅⽹站,我们发现,ElDialog 有三个插槽,因此,我们需要接管这三个插槽1、对 header 的处理
import Vue from "vue";
import Component from "vue-class-component";
@Component({
name: "AbstractDialog",
})
class AbstractDialog extends Vue {
/*
构建弹窗的Header
*/
_createHeader(h) {
// 判断在调⽤的时候,外界是否传⼊header的插槽,若有的话,则以外界传⼊的插槽为准
var slotHeader = this.$scopedSlots["header"] || this.$slots["header"];
if (typeof slotHeader === "function") {vue element admin
return slotHeader();
}
//若⽤户没有传⼊插槽,则判断⽤户是否想改写Header
var renderHeader = derHeader;
if (typeof renderHeader === "function") {
return
{renderHeader(h)}
;
}
//如果都没有的话, 返回undefined,则dialog会使⽤我们预设好的title
}
}
2、对 body 的处理
import Vue from "vue";
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论