建议 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
| const messageInstance = new Map< ComponentPublicInstance<{ doClose: () => void }>, { 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> { return new Promise((resolve, reject) => { const vm = showMessage( options, appContext ?? (MessageBox as IElMessageBox)._context ) messageInstance.set(vm, { options, callback, resolve, reject, }) }) }
|
直接看到返回 promise 这一部分,我们可以看到在 Promise 的 executor
函数中, resolve
和 reject
被 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
| options.onAction = (action: Action) => { 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
| 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) getAppendToElement(props).appendChild(container.firstElementChild!) return vnode.component }
vm.visible = true
|
我们可以看到 options
在 initInstance
中作为 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
其中的 resolve
和 reject
被挂载到了 messageInstance
实例上,并在 action
事件中直接获取该 messageInstance
进行 resolve
和 reject
达到交互型的 promise 效果。
另外也可以稍微看一下 messageBoxFactory
工厂函数是如何实现 MessageBox.confirm
几个函数的,实现并不复杂,对未来封装工厂函数具有一定启发。