实现一个灵活的组件,我们需要关注两点
- props的设计
- slot插槽的使用
简单来说,props就是实现属性值的传递,slot则是实现子节点的传递,如果vue能像react那样可以使用childre传递子节点,也就不需要slot了
但是要想实现一个扩展性好,并且易维护的组件,我们需要考虑的就更多了
- 如何组织css
- 如何实现去其他组件的配合联动,比如
el-form
下面嵌套el-button
下面我们从这四个角度,来看下el-button
的源码
css
的组织和规范
如果是简单的项目,css也许不需要什么规范。 但是当项目维护的人比较多的时候,有一些通用的规范,大家能够相互遵守,并且能够在短时间理解对方的代码,降低合作的成本就至关重要。
element-plus
采用的是bem
的css的规范,并且在全局提供了一个useNamespace
的hooks能够让我们快速实现bem
的开发。
bem
规范大家都了解,但是我们怎么能提高使用bem的效率呢,可以参考下useNamespace
的实现
const _bem = (
namespace: string,
block: string,
blockSuffix: string,
element: string,
modifier: string
) => {
let cls = `${namespace}-${block}`
if (blockSuffix) {
cls += `-${blockSuffix}`
}
if (element) {
cls += `__${element}`
}
if (modifier) {
cls += `--${modifier}`
}
return cls
}
我们可以看到在element
在定义bem
的时候有5个元素,其中block
,element
, modifier
是bem
规范中定义的
那么namespace
和blockSuffix
对应又是什么呢?
namespace
表示组件的命名空间, 比如element
的所有组件都是以el-
开头blockSuffix
表示的是,跟这个组件相关的,或者相耦合的组件,比如el-button
与el-button-gropu
其中group
就是blockSuffix
可以看到element
在使用bem
的同时,也结合实际的业务加以扩充。
可以打的小抄,代码位于packages/hooks/use-namespace/index.ts
export const useNamespace = (block: string) => {
const globalConfig = useGlobalConfig('namespace')
const namespace = computed(() => globalConfig.value || defaultNamespace)
const b = (blockSuffix = '') =>
const e = (element?: string) =>
const m = (modifier?: string) =>
const be = (blockSuffix?: string, element?: string) =>
const em = (element?: string, modifier?: string) =>
const bm = (blockSuffix?: string, modifier?: string) =>
const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
const is: {
(name: string, state: boolean | undefined): string
(name: string): string
} = (name: string, ...args: [boolean | undefined] | []) => {
const state = args.length >= 1 ? args[0]! : true
return name && state ? `${statePrefix}${name}` : ''
}
可以在写组件的时候,更欢快的使用bem
button
ref="_ref"
:class="[
ns.b(),
ns.m(_type),
ns.m(_size),
ns.is('disabled', _disabled),
ns.is('loading', loading),
ns.is('plain', plain),
ns.is('round', round),
ns.is('circle', circle),
]"
:disabled="_disabled || loading"
:autofocus="autofocus"
:type="nativeType"
:style="buttonStyle"
@click="handleClick"
>
```