[toc]
# 1. 开始
组内很多项目是Vue写的,之前页面调用弹窗很麻烦,要引入弹窗组件,在data
中声明showDialog
,在methods
中声明onShowDialog
、onCloseDialog
等至少两个方法,然后在template
中还要写:
<Dialog
v-if="showDialog"
:data="someData"
@onCloseDialog="onCloseDialog"
/>
可以看到引入一个弹窗要加这么多东西,既费劲又不容易维护。
# 2. 原理
# 2.1. H5
H5的核心是利用了Vue.extend
,将弹窗组件作为构造器,生成一个子类 (opens new window)。核心代码如下:
function initInstance() {
const dialogId = 'press-dialog';
const oldDialog = document.getElementById(dialogId);
if (oldDialog) {
oldDialog.parentNode.removeChild(oldDialog);
}
const dialogRootDiv = document.createElement('div');
dialogRootDiv.id = dialogId;
document.body.appendChild(dialogRootDiv);
const instance = new (Vue.extend(VueDialog))({
el: dialogRootDiv,
});
return instance;
}
# 2.2. 小程序
uni-app
版本的小程序可以例用selectComponent
获取实例,核心代码如下:
const context = options.context || getContext();
const dialog = context.selectComponent(options.selector);
# 2.3. Promise
此外要让组件支持Promise
的函数调用,即点击确定进入then
方法,点击取消进入catch
,有两种方案:
# 2.3.1. 组件内部声明showDialog方法
handler.js
中:
let instance;
const Dialog = (options) => {
instance = getContext().selectComponent('#press-dialog');
return instance.showDialog(options).then((val) => {
instance = null;
return Promise.resolve(val);
})
.catch((err) => {
instance = null;
return Promise.reject(err);
});
};
组件中:
data() {
return {
resolve: '',
reject: '',
promise: '',
show: false,
title: t('dialog.title'),
content: '',
};
}
methods: {
// 弹出messageBox,并创建promise对象
showDialog(options) {
this.show = true;
Object.keys(options).map((key) => {
this[key] = options[key];
});
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
// 返回promise对象
return this.promise;
}
}
这种方法的缺点是,组件内要写重复的showDialog
方法,还要在data
中声明promise、resolve、reject
等。
# 2.3.2. handler中动态注入callback
handler.js
中:
const Dialog = (options) => {
return new Promise((resolve, reject) => {
const dialog = getContext().selectComponent(options.selector);
if (dialog) {
const newOptions = {
callback: (action, instance) => {
action === 'confirm' ? resolve(instance) : reject(instance);
},
...options,
};
dialog.setData(newOptions);
Vue.nextTick(() => {
dialog.setData({ show: true });
});
}
});
};
组件中:
methods: {
close(action) {
this.dataShow = false;
this.$nextTick(() => {
this.$emit('close', action);
const { callback } = this;
if (callback) {
callback(action, this);
}
});
},
}
这种方法的缺点就是逻辑比较绕,相对难理解些。