这是我参加第五届青训营伴学笔记创作活动的第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动态赋给class
,style
的绑定也是如此,这里的style的运算主要是根据父组件的type
传来的属性计算它的背景颜色等属性。这样,一个基本的Button的标签结构就完成了。
Script部分
script里的内容主要有以下几个部分:
-
定义props
-
获取命名空间
-
根据命名空间获取类名集合
-
动态修改样式
定义props
定义一个props很简单,之前也说过,写好一个类似在选项式API
中props
结构的对象,然后将它传给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-colo
r这个变量属性。我认为这里用变量的原因是,如果直接使用background-color
,语义不够直接,写起来较为麻烦,而用规定好的命名规范,写起来体验会比直接写原来的css属性要好。
当我们有这样一个css的写法之后,我们需要在js中,判断type去修改它的颜色。我会在namespace
中,定义一个方法,接受一个对象作为参数,键为要改变样式的除去前缀的属性名(例如bg-color
),键值为具体的属性值。在这个方法中,会遍历对象每一个属性,将键值与前缀(namespace中)拼接,并且将具体属性值赋给这个对象属性,最后将这个对象返回给组件的styles。这样,动态修改样式的工作就完成了。
总结
一个button的制作过程如上所示。这只是一个普通的button,并未提及大小、圆形、椭圆等button的写法,但思路大同小异。以上就是我对button组件的理解。