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 小时前
TypeScript 类型魔术:模板字面量类型的深层解密与工程实践
前端·javascript·ubuntu·架构·typescript·前端框架
Nan_Shu_6148 小时前
学习: Threejs (2)
前端·javascript·学习
G_G#9 小时前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
@大迁世界9 小时前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路9 小时前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug9 小时前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu121389 小时前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中9 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路9 小时前
GDAL 实现矢量合并
前端
hxjhnct9 小时前
React useContext的缺陷
前端·react.js·前端框架