【ElementPlus源码】Button 按钮

看源码时候做的笔记。

准备工作

本地开发 | Element Plus (element-plus.org)

文档与源码对应:docs/examples/button

在这里插入图片描述
组件源码位置: packages/components/button/src

属性

定义在 packages/components/button/src/button.ts(下面路径packages/components/button省略。)

以下面三个属性为例。buttonProps 定义了button有哪些属性,和属性的类型。如size的类型是useSizeProp,即是字符串类型的,values值是枚举值,非必填的属性。

export const buttonProps = buildProps({
  /**
   * @description button size
   */
  size: useSizeProp,
  /**
   * @description disable the button
   */
  disabled: Boolean,
  /**
   * @description button type
   */
  type: {
    type: String,
    values: buttonTypes,
    default: '',
  })

useSizeProp

export const useSizeProp = buildProp({
  type: String,
  values: componentSizes,
  required: false,
} as const)

componentSizes

export const componentSizes = ['', 'default', 'small', 'large'] as const

在这里插入图片描述

buildProps是一个构建属性的方法,没看懂。

buttonProps -> prop:从buttonProps到button.vue 的props

// buttonProps -> ButtonProps 
// src / button.ts
export type ButtonProps = ExtractPropTypes<typeof buttonProps>

// ButtonProps -> useButton 
// src / use-button.ts
export const useButton = (
  props: ButtonProps,
  emit: SetupContext<ButtonEmits>['emit']
) => {...}

// buttonProps-> props 
// src / button.vue
const props = defineProps(buttonProps)

// src / button.vue
// 用props创建button实例,并解构出一堆属性
const { _ref, _size, _type, _disabled, _props, shouldAddSpace, handleClick } =
  useButton(props, emit)

调用useButton创建button实例,解构出一堆属性。

这些属性一部分绑定到class上,如:_size, _type, _disabled其他props的属性 。用于控制按钮的样式

// useNamespace,用于生成 BEM(Block Element Modifier)命名规则的类名和 CSS 变量名
const ns = useNamespace('button')

//  生成符合 BEM 命名规则的类名,绑定到元素的class上
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),
  ... // 其他props的属性
])

// 绑定在class上
 <component
    :is="tag"
    ref="_ref"
    v-bind="_props"
    :class="buttonKls" // 这里
    :style="buttonStyle"
    @click="handleClick"
  >

一部分绑定到属性上:v-bind="_props"

解构出的点击事件handleClick绑定到@click上。

解构出的shouldAddSpace通过defineExpose暴露出去:

// button.vue
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方法:

在这里插入图片描述
可以通过this.refs.refName调用看到:

// template
<el-button ref="buttonRef">Button</el-button>
// script
console.log(this.$refs.buttonRef);

在这里插入图片描述

方法

buttonEmits -> button实例。

// src / button.ts
export const buttonEmits = {
  click: (evt: MouseEvent) => evt instanceof MouseEvent,
}

// src / button.vue
const emit = defineEmits(buttonEmits)
const { _ref, _size, _type, _disabled, _props, shouldAddSpace, handleClick } =
  useButton(props, emit)

解构出的handleClick 是方法,绑定在@click上:@click="handleClick"

模板

  • tag默认为button(在button.ts中定义的buttonProps中)
  • _ref为ref<HTMLButtonElement>(),在use-button.ts中定义的useButton中
  • _props为useButton创建的实例解构出的属性,动态绑定到模板上
  • buttonKls为生成的符合 BEM 命名规则的类名,绑定到class上
  • buttonStyle为根据props生成的样式,绑定在style上
  • handleClick为解构出来的点击事件

template、el-icon、span 对应插槽。

<template>
  <component
    :is="tag"
    ref="_ref"
    v-bind="_props"
    :class="buttonKls"
    :style="buttonStyle"
    @click="handleClick"
  >
    <!-- 插槽-loading -->
    <template v-if="loading">
      <slot v-if="$slots.loading" name="loading" />
      <el-icon v-else :class="ns.is('loading')">
        <component :is="loadingIcon" />
      </el-icon>
    </template>
    <!-- 插槽-icon -->
    <el-icon v-else-if="icon || $slots.icon">
      <component :is="icon" v-if="icon" />
      <slot v-else name="icon" />
    </el-icon>
    <!-- 插槽-default -->
    <span
      v-if="$slots.default"
      :class="{ [ns.em('text', 'expand')]: shouldAddSpace }"
    >
      <slot />
    </span>
  </component>
</template>

使用到的hooks

路径:packages/hooks/

use-prop

路径:/use-prop

作用:传入一个name,返回实例的name属性。

获取当前Vue实例,返回一个计算属性,其值是:实例的某个属性。
vm.proxy 是当前 Vue 实例的代理对象,$props 是代理对象的属性集合,[name] 是指定的属性。

import { computed, getCurrentInstance } from 'vue'
import type { ComputedRef } from 'vue'

export const useProp = <T>(name: string): ComputedRef<T | undefined> => {
  // 获取当前Vue实例
  const vm = getCurrentInstance()
  // 返回当前实例的某个属性
  return computed(() => (vm?.proxy?.$props as any)?.[name])
}

useDeprecated

路径:use-deprecated

作用:显示已弃用的警告

// 显示已弃用的警告
export const useDeprecated = (
  { from, replacement, scope, version, ref, type = 'API' }: DeprecationParam,
  condition: MaybeRef<boolean>
) => {
  watch(
    () => unref(condition), // 如果condition是ref,就返回这个ref的内部值;否则返回它本身
    (val) => {
      if (val) {
        debugWarn(
          scope,
          `[${type}] ${from} is about to be deprecated in version ${version}, please use ${replacement} instead.
For more detail, please visit: ${ref}
`
        )
      }
    },
    {
      immediate: true,
      // 无论 condition 的初始值是什么,回调函数都会在创建侦听器时立即执行一次
    }
  )
}

如:在button中,当传入的type为text时,props.type === 'text'返回true,就会触发useDeprecated 中的watch(),告诉用户text要被弃用,建议用link替代。

useDeprecated(
  {
    from: 'type.text',
    replacement: 'link',
    version: '3.0.0',
    scope: 'props',
    ref: 'https://element-plus.org/en-US/component/button.html#button-attributes',
  },
  computed(() => props.type === 'text')
)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值