Vue3组件库开发第二章,BEM规范

151 阅读3分钟

什么是 BEM 规范

BEM(Block、Element、Modifier)是 CSS 命名规范。Bem 是块(block)、元素(element)、修饰符(modifier)的简写。其实真是使用起来不只是这三个词,而是还有其他的,比如:块(block)、元素(element)、修饰符(modifier)、状态(state)、主题(theme)、主题元素(theme element)、主题修饰符(theme modifier)、主题状态(theme state)等。这里我来举一个例子。比如有如下卡片结构:

<div class="sb-card-v2 sb-card-v2--theme_dark">
  <h2 class="sb-card-v2__title">卡片标题</h2>
  <p class="sb-card-v2__content">卡片内容</p>
</div>

<div class="sb-card-v2 sb-card-v2--theme_light">
  <h2 class="sb-card-v2__title">卡片标题</h2>
  <p class="sb-card-v2__content">卡片内容</p>
</div>

以上代码中 sb-card-v2 是块,其中 v2 是 blockSuffix,这个 blockSuffix 是为了解决 block 重复的问题,比如有另一个 block 也是 sb-card-v2,那么就会有冲突,所以需要添加后缀。sb-card-v2__titlesb-card-v2__content 是块 sb-card-v2 的元素,这里的 title 和 content 是 element。sb-card-v2--theme_darksb-card-v2--theme_light 是块 sb-card-v2 的修饰符,这里的 dark 和 light 是 modifier,后面的 light 和 dark 就是 modifier 的值。

创建生成 BEM 命名的工具

创建 hooks/index.js 用来导出所有的 hooks,然后创建 hooks/useNamespace/useNamespace.js 用来导出 useNamespace useNamespace 函数用来创建 BEM 命名。具体代码如下:

export const defaultNamespace = "x";

export const useNamespace = (blocks) => {
  const namespace = defaultNamespace;

  const block = (blockSuffix) => {
    return _bem(namespace, blocks, blockSuffix, "", "", "");
  };

  const element = (element) =>
    element ? _bem(namespace, blocks, "", element, "", "") : "";

  const modifier = (modifier, value) =>
    modifier ? _bem(namespace, blocks, "", "", modifier, value) : "";

  const is = (activeName, active) =>
    activeName && active ? `is-${activeName}` : "";

  return {
    namespace,
    block,
    element,
    modifier,
    is,
  };
};

const _bem = (
  namespace,
  block,
  blockSuffix,
  element,
  modifier,
  modifierValue
) => {
  let cls = `${namespace}-${block}`;
  blockSuffix && (cls += `-${blockSuffix}`);
  element && (cls += `__${element}`);
  modifier && (cls += `--${modifier}`);
  modifierValue && (cls += `_${modifierValue}`);

  return cls;
};

以上代码看上去很复杂其实一点也不简单(一点也不难),我带大家分解一下。

  • export const defaultNamespace = "x";: 这段代码其实就是创建了一个默认的组件名前缀,比如 elementUI 组件库的前缀是 el,里面的每一个组件都是 el-xxx 的形式,比如 el-button,el-input,el-card 等等。
  • _bem:这个函数就是用来创建 bem 命名的,它接收 6 个参数,第一个参数是组件名前缀,第二个参数是块名,第三个参数是块的后缀,第四个参数是元素名,第五个参数是修饰符名,第六个参数是修饰符的值,这个函数是不暴露的。
  • useNamespace: 这个函数就是用来创建 bem 命名的,它接收一个参数,这个参数就是块名,这个函数返回一个对象,对象中有四个方法,分别是 block,element,modifier,is。
  • block: 这个函数就是用来创建块的,它接收一个参数,这个参数就是块的后缀,这个函数返回一个 bem 命名。
  • element: 这个函数就是用来创建元素的,它接收一个参数,这个参数就是元素的名字,这个函数返回一个 bem 命名。
  • modifier: 这个函数就是用来创建修饰符的,它接收两个参数,第一个参数就是修饰符的名字,第二个参数就是修饰符的值,这个函数返回一个 bem 命名。
  • is: 这个函数就是用来创建状态的,它接收两个参数,第一个参数就是状态的名字,第二个参数就是状态的值,这个函数返回一个 bem 命名。这个函数后面会用到这里先不做演示。

最后在 hooks/index.js 中导出 useNamespace。

export * from "./useNamespace/useNamespace";

使用 BEM 规范

在 button 组件中我们使用一下上面写的 hooks。代码如下:

<script setup>
  import { useNamespace } from "@ui-library/hooks";

  defineOptions({
    name: "XButton",
  });

  // 调用一下userNamespace
  const ns = useNamespace("button");

  consoe.log(ns.block()); // x-button
  consoe.log(ns.element("content")); // x-button__content
  consoe.log(ns.modifier("size", "small")); // x-button--size_small
  consle.log(ns.is("disabled", true)); // is-disabled
</script>

<template>
  <button :class="[ns.block(),  ns.modifier("size", size), ns.modifier("size", "small")]">
    <span>
      <slot>button</slot>
    </span>
  </button>
</template>

统过以上在:class="[ns.block(), ns.modifier("size", size),ns.modifier("size", "small")]" 来使用 BEM 规范,这时候 button 这个按钮就会加上 class,值比如 x-button--size_small。后面带大家写样式的时候就会看到效果了。