eleme vue msgbox 简单看看实现原理

依赖版本

element-ui 版本 10592d12ea981912165542920160669fd8874bd9

文档 https://element.eleme.io/#/zh-CN/component/message-box

要解决的问题

像是

1
2
3
4
$msgbox(options)
$alert(message, title, options) 或 $alert(message, options)
$confirm(message, title, options) 或 $confirm(message, options)
$prompt(message, title, options) 或 $prompt(message, options)

这样的api是怎么实现的

代码阅读

大概看一眼怎么搞的

src/index.js 看 这些方法 ,都是来自 import MessageBox from '../packages/message-box/index.js';

1
2
3
4
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;

那么看 packages/message-box/src文件夹

两个文件 main.jsmain.vue

main.vue瞄一眼,都是我们常见的写法

main.js,看这些方法 最后 都是 调用 MessageBox(配置参数)的形式调用

再看const MessageBox = function(options, callback) {的实现

都是 先msgQueue.push(配置参数) + showNextMsg()

msgQueueshowNextMsg,分别是个数组,和从数组中shift() 取出值 进行具体展示执行

然后 看 showNextMsg()

1
2
3
if (!instance) {
initInstance();
}

然后

1
2
3
4
5
6
7
const initInstance = () => {
instance = new MessageBoxConstructor({
el: document.createElement('div')
});

instance.callback = defaultCallback;
};

1
const MessageBoxConstructor = Vue.extend(msgboxVue);

说明了基本这就是 工厂模式+单例模式+Vue.extend来创建弹框单例,所以我们页面上不需要写什么,就能直接调用

然后这些confirm调用过程 也就是对这个单例的参数改动 比如控制样式 数据 显示之类的

总结

  1. 用户调用 $msgbox
  2. 调用到packages/message-box/src/main.jsMessageBox(options,callback)
  3. MessageBox(options,callback)通过 数组 + 数组.shift,依次提取数据展示
  4. 对于展示的实例 是 通过 Vue.extend(packages/message-box/src/main.vue) + 工厂单例来产生的一个实例
  5. 他们 搞了一个element/examples/components/demo-block.vue 可以在markdown写示例代码,并且在网页上查看 可以运行 66666

那常见的问题可能有说,连续调用2此带有回调函数的$msgbox会怎样

我们可以看showNextMsg()实现

1
2
3
4
5
6
7
8
9
10
11
12
13
//得到 instance
if (!instance.visible || instance.closeTimer) {
//...
let oldCb = instance.callback;
instance.callback = (action, instance) => {
oldCb(action, instance);
showNextMsg();
};
//...
Vue.nextTick(() => {
instance.visible = true;
});
//...

意思就是如果我们同步调用两个,

  • 因为我们visible设置为true是 异步里发生的,那么 这两个调用时 visible默认都是false,所以 只会最后一个生效
  • 如果我们异步调用两次msgbox,那么 后一个调用 会在!visible的地方为false,不会直接触发,但是因为设计了msgQueue,因此 会放在数组中,然后看到 上面的callback,会在调用oldCb以后 再调用showNextMsg,所以会在对话框回调以后 再回调 那个时刻,msgQueue里首个 并从数组中移除

总结

总流程实现

也就是方法是src/index.js: Vue.prototype.方法名 = 方法函数 来让全局可用

通过 document.createElement('div') 和 全局变量, 创建绑定维护对应的单例实例.

这样就实现了 任何页面,不需要写组件,就能this.方法名(参数) 来弹窗

适用场景,不需要业务定UI,否则需要页面v-slot之类插入业务内容

参数设计(核心参数与可选参数与回调)

核心参数是函数的直接参数

可选参数组合成一个对象,提供默认值

回调既有成功也有失败,随是promise,总觉得用reject来做取消和关闭其实不太好, 可以全resolve走自定义状态

提供一定的重载实现

内部具体

队列, 同步异步, callback持有