痛点
当我们在单个页面中存在多个弹窗组件时,一般我们会对其进行封装处理,而此时页面会出现多个组件块,对于其组件命名、传参命名、事件命名等往往会显得冗长且让人头疼,而且当多个页面调用同一个组件时会产生多余的重复代码。对于我而言,更习惯使用js去解决不必要html,因此在此基础上,想到了擅长使用在独立组件开发场景中的Vue.extend
+$mount
。
初步尝试
首先我们弄清楚我们想要到达的效果是什么样的,既然是点击调用弹窗,那么我们需要在点击的时候通过js将组件实例化并将其渲染挂载到body节点上。
将我们要使用的弹窗组件封装成DialogA.vue(此处以elementUI为例)
<template>
<el-dialog
title="测试弹窗"
:visible="dialogABool"
>
<div class="content">
<!-- todo -->
</div>
</el-dialog>
</template>
<script>
import { Component, Vue } from 'vue-property-decorator'
@Component({})
export default class DialogA extends Vue {
dialogABool = false
}
</script>
在其同级目录下新建index.js用来实例挂载
import Vue from 'vue';
import dialogA from './DialogA';
const DialogA = Vue.extend(dialogA);
var instance = null;
export function alertDialogA(){
if(!instance){
instance = new DialogA();
instance.$mount();
document.body.appendChild(instance.$el);
}
instance.dialogABool = true;
}
这样我们就可以直接在其他组件中通过调用alertDialogA()
来控制弹窗组件。
传参与回调
传参比较简单,既然是一个方法自然可以接收参数alertDialogA(params)
,因为extend创建的Vue构造器继承了DialogA组件的属性,方法等,因此在index.js中将参数赋值给DialogA中变量即可。
而想要接收回调,我们第一时间想到使用Promise
来完成,将index.js改造一下:
export function alertDialogA(params){
if(!instance){
instance = new DialogA();
instance.$mount();
document.body.appendChild(instance.$el);
}
instance.dialogABool = true;
instance.params = params;
return new Promise((resolve,reject)=>{
instance.promise = {
resolve,reject
}
})
}
在DialogA中通过调用this.promise.resolve()
来触发‘完成动作’,在调用弹窗组件中通过then方法实现回调,即alertDialogA(params).then(()=>{})
,当然此处也可以传递回调参数使得组件使用起来更加灵活。
运用思考
可以看见我们在index.js中将var instance = null;
放在了alertDialogA
方法外部,并且在方法里面进行了非空判断,这也就意味着只有在页面首次加载的时候才会渲染并加载弹窗组件,这样当我们进行多次关闭-打开弹窗组件操作时,组件内的变量存在被污染的情况,此时我们可以内置一个钩子函数init()来初始化数据,从而使组件在不重新渲染的条件下可以正常使用。
当我们需要每次打开弹窗都需要重新渲染时,一来可以将var instance = null;
放在方法内部调用并放开判断,二来可以通过在DialogA.vue中添加v-if来达到此效果。
完整代码
DialogA.vue
<template>
<el-dialog
title="测试弹窗"
:visible="dialogABool"
>
<div class="content">
<!-- todo -->
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="onSubmit">确定</el-button>
</span>
</el-dialog>
</template>
<script>
import { Component, Vue } from 'vue-property-decorator'
@Component({})
export default class DialogA extends Vue {
dialogABool = false
params = null
init() {
// 初始化数据
}
onSubmit() {
// todo
this.promise.resolve(data)
}
}
</script>
index.js
import Vue from 'vue';
import dialogA from './DialogA';
const DialogA = Vue.extend(dialogA);
var instance = null;
export function alertDialogA(params){
if(!instance){
instance = new DialogA();
instance.$mount();
document.body.appendChild(instance.$el);
}
instance.dialogABool = true;
instance.params = params
// 获取页面数据
instance.init();
return new Promise((resolve,reject)=>{
instance.promise = {
resolve,reject
}
})
}
调用弹窗组件页面
alertDialogA({name:'测试数据'}).then((data)=>{console.log(data)})
总结比较
熟悉elementUI的人不难发现,这种用法和轻量级的弹出层this.$confirm()
、this.$alert()
、this.$message()
等用法类似,方便的同时也满足了弹出复杂内容的要求,可谓是一举两得。
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: dongfangyiyu 原文链接:https://juejin.im/post/6959449865470345252