跟element-plus学习写组件,第一章(el-button)

526 阅读1分钟

实现一个灵活的组件,我们需要关注两点

  1. props的设计
  2. slot插槽的使用

简单来说,props就是实现属性值的传递,slot则是实现子节点的传递,如果vue能像react那样可以使用childre传递子节点,也就不需要slot了

但是要想实现一个扩展性好,并且易维护的组件,我们需要考虑的就更多了

  1. 如何组织css
  2. 如何实现去其他组件的配合联动,比如 el-form下面嵌套el-button

下面我们从这四个角度,来看下el-button的源码

  1. 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, modifierbem规范中定义的

那么namespaceblockSuffix对应又是什么呢?

  • namespace表示组件的命名空间, 比如element的所有组件都是以el-开头
  • blockSuffix表示的是,跟这个组件相关的,或者相耦合的组件,比如el-buttonel-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"
>
```