Element-Plus源码分析--button组件

引言

再对Elements-Plus的设计框架有了初步了解过后 也通过对最基础的组件elIcon的设计源码进行分析之后,理解了其中组件设计的流程。那么再继续提高组件的复杂度继续看看elButton组建的实现。 组件目录如下

js 复制代码
├── packages
│   ├── components
│   │   ├── button
│   │   │   ├── __tests__       # 测试目录
│   │   │   ├── src             # 组件入口目录
│   │   │   │   ├── button-custom.ts   # 自定义类型,方法
│   │   │   │   └── button-group.ts    # button-group组件属性与ts类型
│   │   │   │   └── button-group.vue   # button-group组件模板内容
│   │   │   │   └── button.ts       #组件属性与 TS 类型
│   │   │   │   └── button.vue      #组件模板内容
│   │   │   │   └── constants.ts    #常量申明
│   │   │   │   └── instance.ts     #button&button-group的组件类型注册
│   │   │   │   └── use-button.ts   #组件hook
│   │   │   ├── style           # 组件样式目录
│   │   │   └── index.ts        # 组件入口文件
│   │   └── package.json

 

先从基本的button.ts文件开始来看,引入方法、类型

js 复制代码
import { useSizeProp } from '@element-plus/hooks' //引入字符串枚举类型
import { buildProps, definePropType, iconPropType } from '@element-plus/utils' //props优化工具类,组件类型
import { Loading } from '@element-plus/icons-vue'
import type { Component, ExtractPropTypes } from 'vue' //vue提供的props转换方法类型,component类型

定义需要的类型

js 复制代码
export const buttonProps = buildProps({
//
***
  ***
  *****
  
  loadingIcon: {
    type: iconPropType,
    default: () => Loading,
  },//loading图标接收组件类型
  
  ****
  *****
  //是否在汉字之间插入空格
    autoInsertSpace: {
    type: Boolean,
    default: undefined,
  }, //APi中为注明的属性
   /**
   * @description custom element tag  自定义的标签可传入组件 v2.3.4 以上版本新增
   */
  tag: {
    type: definePropType<string | Component>([String, Object]),
    default: 'button',
  },
})

抛出事件类型,优化之后的props属性

js 复制代码
export const buttonEmits = {
  click: (evt: MouseEvent) => evt instanceof MouseEvent,
}
export type ButtonProps = ExtractPropTypes<typeof buttonProps>
export type ButtonEmits = typeof buttonEmits
export type ButtonType = ButtonProps['type']
export type ButtonNativeType = ButtonProps['nativeType']

export interface ButtonConfigContext {
  autoInsertSpace?: boolean
}
button.vue文件
js 复制代码
//动态组件 默认使用button    2.3.4 以上版本新增 tag组件模式 自定义原型标签
<component :is="tag" ref="_ref" v-bind="_props" :class="buttonKls" :style="buttonStyle" @click="handleClick">

loading插槽

icon插槽

默认内容插槽

自定义内容插槽
****

</component>
const { _ref, _size, _type, _disabled, _props, shouldAddSpace, handleClick } =
  useButton(props, emit)
引入自定义事件,
props内容

const ns = useNamespace('button') 使用EBM 定义类
//根据状态设备不同的类名
const buttonKls = computed(() => [
  ns.b(),
  ns.m(_type.value),
  ns.m(_size.value),
  ns.is('disabled', _disabled.value),
  ns.is('loading', props.loading),
  ns.is('plain', props.plain),
  ns.is('round', props.round),
  ns.is('circle', props.circle),
  ns.is('text', props.text),
  ns.is('link', props.link),
  ns.is('has-bg', props.bg),
])
//使用vue3的defineExpose宏 抛出属性,方法
defineExpose({
  /** @description button html element */
  ref: _ref,
  /** @description button size */
  size: _size,
  /** @description button type */
  type: _type,
  /** @description button disabled */
  disabled: _disabled,
  /** @description whether adding space */
  shouldAddSpace,
})

button-group.ts定义button模式下的类型说明

button-group.vue 定义一个个插槽 使用ts定义的属性设置组件接收的prorps,provide 抛出buttonGroup的属性buttonGroupContextKey作为唯一键值

js 复制代码
provide(
  buttonGroupContextKey,
  reactive({
    size: toRef(props, 'size'),
    type: toRef(props, 'type'),
  })
)

那么申明 buttonGroupContextKey 用到的 type(InjectionKey)是什么? InjectionKey 是 Vue 3 组合式 API 中用于依赖注入的键类型,继承自 JavaScript 的 Symbol 类型。它通过类型断言确保 provideinject 函数中使用的键具有明确的类型安全性,主要用于组件间的数据传递; buttonGroupContextKey 的定义在 button/constants.ts的文件中有提到。

instance.ts文件

引入组件并抛出组件实例类型

js 复制代码
import type Button from './button.vue'
import type ButtonGroup from './button-group.vue'

export type ButtonInstance = InstanceType<typeof Button> & unknown
export type ButtonGroupInstance = InstanceType<typeof ButtonGroup> & unknown

最后需要在 button/index.ts 目录文件中定义 Button 组件的出口内容。

js 复制代码
// *****
 *****代码块
export const ElButton: SFCWithInstall<typeof Button> & {
  ButtonGroup: typeof ButtonGroup
} = withInstall(Button, {
  ButtonGroup,
})
export const ElButtonGroup: SFCWithInstall<typeof ButtonGroup> =
  withNoopInstall(ButtonGroup)
export default ElButton

export * from './src/button'
export * from './src/constants'
export type { ButtonInstance, ButtonGroupInstance } from './src/instance'
相关推荐
中微子8 小时前
Vue 2 与 Vue 3 组件写法对比
前端·javascript·vue.js
中微子8 小时前
Vue 3 JavaScript 最佳实践指南
前端·javascript·vue.js
nightunderblackcat8 小时前
四大名著智能可视化推演平台
前端·网络·爬虫·python·状态模式
Mintopia8 小时前
Next.js 与 Serverless 架构思维:无状态的优雅与冷启动的温柔
前端·后端·全栈
小白而已8 小时前
事件分发机制
前端
蝙蝠编人生8 小时前
TailwindCSS vs UnoCSS 性能深度对决:究竟快多少
前端
ZXH01228 小时前
浏览器兼容性问题处理
前端
鹏多多9 小时前
flutter-切换状态显示不同组件10种实现方案全解析
android·前端·ios
向葭奔赴♡9 小时前
HTML的本质——网页的“骨架”
前端·javascript·html