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

相关推荐
胡西风_foxww14 分钟前
【ES6复习笔记】数值扩展(16)
前端·笔记·es6·扩展·数值
mosen86815 分钟前
uniapp中uni.scss如何引入页面内或生效
前端·uni-app·scss
白云~️16 分钟前
uniappX 移动端单行/多行文字隐藏显示省略号
开发语言·前端·javascript
沙尘暴炒饭18 分钟前
uniapp 前端解决精度丢失的问题 (后端返回分布式id)
前端·uni-app
昙鱼32 分钟前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
天天进步201538 分钟前
Vue项目重构实践:如何构建可维护的企业级应用
前端·vue.js·重构
小华同学ai41 分钟前
vue-office:Star 4.2k,款支持多种Office文件预览的Vue组件库,一站式Office文件预览方案,真心不错
前端·javascript·vue.js·开源·github·office
APP 肖提莫42 分钟前
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
java·前端·算法
问道飞鱼1 小时前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
k09331 小时前
vue中proxy代理配置(测试一)
前端·javascript·vue.js