源码解析--MessageBox组件

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

MessageBox使用

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

javascript 复制代码
// 常规写法
ElMessageBox('内容','标题',{}).then().catch()
// 指定样式
ElMessageBox.confirm()
ElMessageBox.alert()
ElMessageBox.prompt()

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

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

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

javascript 复制代码
// 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

javascript 复制代码
// 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作为属性后,接下来需要创建一个实例

javascript 复制代码
// 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几个函数的,实现并不复杂,对未来封装工厂函数具有一定启发。

相关推荐
傻啦嘿哟19 小时前
Python在Excel中创建与优化数据透视表的完整指南
java·前端·spring
拜晨19 小时前
用流式 JSON 解析让 AI 产品交互提前
前端·javascript
浩男孩19 小时前
🍀vue3 + Typescript +Tdesign + HiPrint 打印下载解决方案
前端
andwhataboutit?19 小时前
LANGGRAPH
java·服务器·前端
无限大619 小时前
为什么"Web3"是下一代互联网?——从中心化到去中心化的转变
前端·后端·程序员
cypking19 小时前
CSS 常用特效汇总
前端·css
程序媛小鱼19 小时前
openlayers撤销与恢复
前端·js
Thomas游戏开发19 小时前
如何基于全免费素材,0美术成本开发游戏
前端·后端·架构
若梦plus19 小时前
Hybrid之JSBridge原理
前端·webview
chilavert31819 小时前
技术演进中的开发沉思-269 Ajax:拖放功能
前端·javascript·ajax