在使用vue做业务的时候经常会用到弹框,最快的办法是使用优秀组件库,比如Element-ui中的el-dialog,使用起来也很方便 大概有如下几种做法:
方法一:将dialog和content-body放在一起
child.vue
<el-dialog
title="提示"
:visible.sync="visible"
width="30%"
:before-close="handleClose">
<span>这是一段信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
parent.vue
<child
:visible="visible"
:otherData="otherData"/>
与此同时需要在当前页面写入visible,并且要在关闭的时候清空数据。 (PS:由于外层传入的prop,内部不能进行修改,如果要修改的话要emit到父级进行同步更新)
<child
+ :visible.sync="visible"
:otherData="otherData"/>
close() {
this.$emit('update:visible', false);
}
方法二:将外层dialog放在parent中,抽象content-body内容为一个组件进行导入
child.vue
<span>这是一段内容</span>
parent.vue
<el-dialog
title="提示"
:visible.sync="visible"
width="30%"
:before-close="handleClose">
<child />
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
parent组件中需要定义visible属性,并且要在关闭的时候清空数据
child.vue
resetFields() {
// 清空child数据
Object.assign(this.$data,this.$options.data())
}
parent.vue
handleClose() {
this.visible = false;
this.$refs['child'].resetFields();
}
方法三:结合Vuex状态管理,这样不需要通过attrs或者props传入进行通信,全局维护一个状态
child.js
const viewmodel = {
state: {
visible: false
},
mutations: {
setVisible(state, payload) {
state.visible = payload;
}
}
}
<el-dialog
title="提示"
:visible.sync="visible"
width="30%"
:before-close="handleClose">
<child />
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
...mapState('child', ['visible'])
...mapMutation('child', ['setVisible'])
以上一些思路都让人感觉比较冗余,用是可以用的,但不推荐。如果一个页面有多个弹框的时候就感觉不够优雅,所以需要进行一定的封装。
首先对于一个dialog组件而言,他无非是需要visible来控制显示/隐藏,title来控制弹框标题,content来表示内容区,close来代表外层调用关闭弹框。
props: {
title: {
type: String,
default: ''
},
component: {
type: Function,
default: () => {}
},
close: {
type: Function,
default: () => {}
}
}
methods: {
closeDialog() {
// 清空弹框数据
Object.assign(this.$data, this.$options.data());
// 调用props中的close方法清空数据
this.close();
// 销毁组件,去掉watcher监听
this.$refs.component.$destroy();
}
}
我们定义一个customDialog组件,内部定义data属性visible。
- 我们通过extend生成一个新的sub方法,将sub指向父级原型链,将传入属性merge到sub下的options中,继承父级原型方法,并将sub中的
super指向父级对象。 - 其次对这个方法进行new操作,本质是生成新的vue实例,会经过一系列的init操作。
- 然后对这个vue实例进行
$mount操作,本质是进行虚拟dom的patch操作,返回el对象 - 最后就可以将这个el对象append到body下,就完成了弹框组件的渲染
const DialogConstructor = Vue.extend(CustomDialog);
inst = new DialogConstructor();
inst.visible = true;
inst.$mount();
document.body.appendChild(inst.$el);
问题1:如何能够从外层改变内部弹框属性
通过传入propsData属性
+ const createDialog = propsData => {
const DialogConstructor = Vue.extend(CustomDialog);
inst = new DialogConstructor({
+ propsData
});
inst.visible = true;
inst.$mount();
document.body.appendChild(inst.$el);
+ }
这样我们就可以通过createDialog({title: xxx})来构造不同的弹框
问题2:打开过的弹框,再次打开有需要再次进行构造
将已经打开的弹框实例依次存入一个map中,通过设置唯一的uid值来进行获取
const createDialog = () => {
+ const instance = {};
return propsData => {
const uid = propsData.id || propsData.title;
let inst = instance[uid];
if (inst) {
+ inst.visible = true;
} else {
...
+ instance[uid] = inst;
}
+ return inst;
}
}
除此之外需要如果当前实例propsData有经过更新,则需要对实例进行更新
const createDialog = () => {
return propsData => {
const instance = {};
if(inst) {
inst.visible = true;
+ const originPropsData = mapValues(
+ inst.$options.props,
+ value => value.default
+ );
+ Object.assign(inst.$props, originPropsData, propsData);
}
}
}
问题3:如何让弹框按打开顺序正确关闭
可以将已打开的实例对象存放在一个栈里。每次打开,其实是将实例入栈;每次关闭,其实是将实例出栈。设置一个$closeDialog方法挂到Vue原型链上,主要处理是获取当前实例,调用当前实例的close方法,将当前实例出栈。
const createDialog = () => {
const instance = {};
+ const openedDialogQueue = [];
return propsData => {
const uid = propsData.id || propsData.title;
let inst = instance[uid];
+ Vue.prototype.$closeDialog = () => {
+ const currentInst = openedDialogQueue.pop();
+ currentInst && currentInst.closeDialog();
+ };
if (inst) {
inst.visible = true;
// NOTE: 更新props
const originPropsData = mapValues(
inst.$options.props,
value => value.default
);
Object.assign(inst.$props, originPropsData, propsData);
// NOTE: 主要处理多个弹框打开后关闭的顺序
+ openedDialogQueue.push(inst);
} else {
const DialogConstructor = Vue.extend(CustomDialog);
inst = new DialogConstructor({
propsData
});
inst.visible = true;
inst.$mount();
document.body.appendChild(inst.$el);
openedDialogQueue.push(inst);
instance[uid] = inst;
}
return inst;
};
}
这样一个简单的弹框就实现,以后每次使用只需要写上简短的几行代码既可以搞定
this.$dialog({
id: 'key',
title: '弹框',
component: () => <div>Hello, world!</div>,
})
总结
以上是自己学习网上大佬后总结一些的心得,在一定程度上提高了开发效率。生活和工作中都需要总结,写下这篇文章,一方面为了记录自己的所做所思,另一方面分享给各位一起学习,如果有什么不正确的地方,希望得到大牛们的指正,万分感谢!