建议 clone 源码配合食用,不懂的可以及时跳转至定义进行反复推敲味道更佳

# MessageBox 使用

看源码前先看看实现和效果,确认功能点:

1
2
3
4
5
6
// 常规写法
ElMessageBox('内容','标题',{}).then().catch()
// 指定样式
ElMessageBox.confirm()
ElMessageBox.alert()
ElMessageBox.prompt()

正常的调用逻辑如上,用的比较多的是下面三种,配合 tsx 可以大大提高代码可读性,另外,如果用户点选确定按钮,则会走 then 逻辑,关闭或者取消则会走 catch 逻辑

# 点选按钮走 then 和 catch 逻辑是怎么实现的?

首先我们直接定位到项目的 messageBox 文件,找到 MessageBox 函数(这里删除了与实现无关代码,完整代码请移步源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// MessageBox.ts
const messageInstance = new Map<
ComponentPublicInstance<{ doClose: () => void }>, // marking doClose as function
{
options: any
callback: Callback | undefined
resolve: (res: any) => void
reject: (reason?: any) => void
}
>()
function MessageBox(
options: ElMessageBoxOptions | string | VNode,
appContext: AppContext | null = null
): Promise<{ value: string; action: Action } | Action> {
// 初始化也就是弹窗时,函数返回一个promise,在关闭时(onActions)进行resolve或者reject
return new Promise((resolve, reject) => {
const vm = showMessage(
options,
appContext ?? (MessageBox as IElMessageBox)._context
)
// collect this vm in order to handle upcoming events.
messageInstance.set(vm, {
options,
callback,
resolve,
reject,
})
})
}

直接看到返回 promise 这一部分,我们可以看到在 Promise 的 executor 函数中, resolvereject 被 set 到了 messageInstance 上,该变量相当于缓存 message 函数的上下文

这里还执行了一个 showMessage 的操作,跳转至 showMessage 函数我们可以看到,在 options 上绑定了一个 actions 事件。该事件从 messageInstance 中拿到当前 msg 实例后,便可以直接使用实例已经在前面挂载好的 resolve 和 reject 属性了

我们知道 vue 的事件绑定可以用 @action 写,也可以用 onAction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// showMessage函数中
options.onAction = (action: Action) => {
// messageBox
const currentMsg = messageInstance.get(vm)!
let resolve: Action | { value: string; action: Action }
if (action === 'cancel' || action === 'close') {
if (options.distinguishCancelAndClose && action !== 'cancel') {
currentMsg.reject('close')
} else {
currentMsg.reject('cancel')
}
} else {
currentMsg.resolve(resolve)
}
}

# 如何连接组件与 TS 代码?

将事件添加到 options 作为属性后,接下来需要创建一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// showMessage函数中
const instance = initInstance(options, container, appContext)!

const initInstance = (
props: any,
container: HTMLElement,
appContext: AppContext | null = null
) => {
const vnode = createVNode(
MessageBoxConstructor,
props,
isFunction(props.message) || isVNode(props.message)
? {
default: isFunction(props.message)
? props.message
: () => props.message,
}
: null
)
vnode.appContext = appContext
render(vnode, container) //渲染Dialog
getAppendToElement(props).appendChild(container.firstElementChild!)
return vnode.component
}

vm.visible = true // 将Dialog visible设置为true

我们可以看到 optionsinitInstance 中作为 props,通过 createVNode 函数传给了 MessageBoxConstructor (index.vue),也就是说在 index.vue 中只要 emit('action') ,就会触发 onAction 绑定的函数,也就是将 Promise 进行 resolve 或者 reject

另外 dialog 的渲染与展示也在上面代码中标注出来

# 总结

在调用 MessageBox 时,ElementPlus 通过 createNode 创建并 render 了 vue 组件,并将其 visible 设置为 true ,并且通过传入 options(props) 来绑定 action 事件。调用 MessageBox 函数返回的 promise 其中的 resolvereject 被挂载到了 messageInstance 实例上,并在 action 事件中直接获取该 messageInstance 进行 resolvereject 达到交互型的 promise 效果。

另外也可以稍微看一下 messageBoxFactory 工厂函数是如何实现 MessageBox.confirm 几个函数的,实现并不复杂,对未来封装工厂函数具有一定启发。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

EvilMoOd 微信支付

微信支付

EvilMoOd 支付宝

支付宝