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'
相关推荐
web打印社区13 小时前
网页静默打印热敏小票:从 HTML 到出纸的完整指南
前端·javascript·vue.js·electron·pdf·html
剪刀石头布啊14 小时前
设计一个多计时任务的任务管理功能
前端
ModyQyW15 小时前
vite-plugin-uni-manifest 更新了什么
前端·typescript·uni-app
Asize16 小时前
AI 协作开发新范式:我用 SDD 做了个排版 npm 包
前端·人工智能
kyriewen17 小时前
我把今年流传的前端 AI 面试题整理了一遍——4 类场景题+回答框架(附速查表)
前端·面试·程序员
计算机魔术师18 小时前
国产多模态模型正面硬刚Opus旗舰:差距从30%缩到3%
前端
风骏时光牛马18 小时前
程序员进阶:深度思考,解锁职场成长的底层逻辑
前端
IT_陈寒18 小时前
用了Proxy才发现以前的JavaScript白写了
前端·人工智能·后端
爱丶不疚18 小时前
Eval: Agent 说的 Eval 是什么?从单测、TDD 到 Sentry 聊起
前端·ai编程·vibecoding
求道於盲18 小时前
python中的类型标注
前端