从ElementPlus源码分析Button的实现(二)| 青训营笔记

44 阅读4分钟

这是我参加第五届青训营伴学笔记创作活动的第5天。

之前根据ElementPlus的Button的源码分析了Button组件是如何诞生的,这次我们来自己动手制作的一个Button组件。

标签结构

一个最普通的Button,内容就是存放一行文字,所以只需要在一个Button标签里嵌入span标签即可。但是为了方便以后扩展,可以使用将这个span嵌入一个默认slot里。

<template>
  <button
    :class="classes"
    :style="buttonStyle"
  >
    <slot><span></span></slot>
  </button>
</template>

我觉得element中直接在标签中写判断class不是非常美观,所以直接用一个数组classes动态赋给classstyle的绑定也是如此,这里的style的运算主要是根据父组件的type传来的属性计算它的背景颜色等属性。这样,一个基本的Button的标签结构就完成了。

Script部分

script里的内容主要有以下几个部分:

  1. 定义props

  2. 获取命名空间

  3. 根据命名空间获取类名集合

  4. 动态修改样式

定义props

定义一个props很简单,之前也说过,写好一个类似在选项式APIprops结构的对象,然后将它传给defineProps即可。

export const buttonProps = {
  type: {
    type: String,
    default: ''
  },
  size: {
    type: String
  }
  ...
}

const props = defineProps(buttonProps)

这样,Button组件的Props定义完成。

最后,别忘记,如果还有将props定义成类型的需求,可以使用ExtractPropTypes来提取类型。

type ButtonProps = ExtractPropTypes<typeof buttonProps>

获取命名空间

我们可以另起一个文件夹hooks,里面存放一些钩子函数,像useNamespace就是一个钩子函数。

在namespace中,你可以添加任何与该组件命名有依赖的属性,最后合成为一个对象返回给组件实例即可。

这里展示添加类名前缀以及拼接修饰符的写法。

const useNamespace = (componentName: string) => {
  const baseName = `y-${componentName}`

  const addModifier = (modifier: string | undefined): string => {
    return modifier === ''? '' : `${baseName}--${modifier}`
  }

  return {
    baseName,
    addModifier,
  }
}

这里的前缀baseName是称为一个组件的基本的名字,像button就会以y-button命名前缀,然后再添加对应的元素或者修饰符(bem命名规范)。这样我们就能获取一个组件的命名空间,可以根据它来做拼接类名、css变量名等操作。

根据命名空间获取类名集合

获取到命名空间之后,我们就可以通过命名空间里的方法来拼接类名,例如它的type是primary,就在前缀的后面加上--primary,这样就能从写好的css里获取到对应的样式(背景颜色、字体颜色等)。最后别忘记加上原本的button,如果你有写button的默认样式的话。

const classes: string[] = [
  // baseName: y-button
  ns.baseName,
  ns.addModifier(_type),
  ns.addModifier(_size)
]

动态修改样式

假设我们在button和全局样式的css里写了如下代码:

/* 全局样式css */
--color-primary: skyblue;

/* button.css */
--y-button--bg-color: #fff;
background-color: var(--y-button-bg-color);

我们想要的效果是,不同的type会展示不同的背景颜色。所以,我会在全局定义好不同type的背景颜色,然后根据每个组件的type,改变--y-button-bg-color这个变量属性。我认为这里用变量的原因是,如果直接使用background-color语义不够直接,写起来较为麻烦,而用规定好的命名规范,写起来体验会比直接写原来的css属性要好。

当我们有这样一个css的写法之后,我们需要在js中,判断type去修改它的颜色。我会在namespace中,定义一个方法,接受一个对象作为参数,键为要改变样式的除去前缀的属性名(例如bg-color),键值为具体的属性值。在这个方法中,会遍历对象每一个属性,将键值与前缀(namespace中)拼接,并且将具体属性值赋给这个对象属性,最后将这个对象返回给组件的styles。这样,动态修改样式的工作就完成了。

总结

一个button的制作过程如上所示。这只是一个普通的button,并未提及大小、圆形、椭圆等button的写法,但思路大同小异。以上就是我对button组件的理解。