4.4 BEM规则封装

447 阅读3分钟

编写组件的时候如果通过手写classname类名,会经常写到BEM命名规则所提及的“-”、“--”、“__”和“前缀”,会变得非常繁琐。但在4.2节已经了解了BEM命名规则的规律,因此可以把BEM命名规则封装成函数动态生成UI组件库的class类名,如代码清单所示。

/**
 * BEM命名字符拼接函数
 * @param { String } namespace 命名空间
 * @param { String } block 块
 * @param { String } blockSuffix 子级块
 * @param { String } element 元素
 * @param { String } modifier 修改器
 * @param { String } modifierValue 修改器的值
 * @returns
 */
const _bem = (namespace, block, blockSuffix, element, modifier, modifierValue) => {
  // 默认Block(块)
  let className = `${namespace}-${block}`;
  // 如果存在子级块
  blockSuffix && (className += `-${blockSuffix}`);
  // 如果存在元素
  element && (className += `__${element}`);
  // 如果存在修改器
  modifier && (className += `--${modifier}`);
  // 如果存在修改器的值
  modifierValue && (className += `_${modifierValue}`);
  // 返回
  return className;
};

use-namespace/index.js文件定义_bem函数,分别接收6个参数:namespace(命名空间), block(块),blockSuffix(子级块)、element(元素)、modifier(修改器)、modifierValue(修改值)。

定义变量className获取Block的类名,也就是参数namespace和block结合“-”拼接生成的类名,如a-tabs(第4行);子级块则用变量className和参数blockSuffix结合“-”拼接生成,如a-tabs-wrap(第6行);元素同样是用变量className和参数element结合“__”拼接生成,如a-tabs-wrap__item(第8行);修改器使用变量className和参数modifier结合“--”拼接生成(第10行);修改值则使用“_”,如a-tags-wrap__item--size_mini(第12行)。最后返回处理完成的变量className(第14行)。

4.4.1 生成Block

生成Block是指定当前组件所使用的类名是什么,根据定义的_bem函数优先生成Block类名,如代码清单所示。

> packages/hook/use-namespace/index.js
export const useNamespace = (block) => {
    const namespace = defaultNamespace;
    // 生成Block(块)
    const block = (blockSuffix = '') => _bem(namespace, block, blockSuffix)
    return {
        namespace,
        block
    };
};

> packages/components/button/src/index.vue:template
<button :class="ns.b()">这是一个测试的按钮</button>  

> packages/components/button/src/index.vue:script
import { useNamespace } from '@ui-library/hook';
const ns = useNamespace('button')
console.log('命名空间>>>', ns.b())

useNamespace函数中定义b方法调用_bem函数并传入namespace、block、blockSuffix参数便可获取到Block类名(第5行),然后返回出去(第8行)。

button组件调用useNamespace方法并传入“button”(第17行),也就是表明当前组件是一个button按钮的Block,接着就是调用“b”方法生成类名(第18行),可以直接为按钮绑定类名(第13行),如图4-6所示。

图片1.png

图4-6 生成button按钮块类名

4.4.2 生成元素Element、修改器Modifier

生成Element和Modifier与生成Block的方式一致,同样是定义元素和修改器两个方法生成className类名并返回,调用时即会生成相对应的class类名,如代码清单所示。

> packages/hook/use-namespace/index.js
export const useNamespace = (block) => {
  const namespace = defaultNamespace;
  // 生成Block(块)
  const b = (blockSuffix = "") => _bem(namespace, block, blockSuffix);
  // 生成Element(元素)
  const e = (element) => element ? _bem(namespace, block, "", element, "") : "";
  // 生成Modifier(元素)
  const m = (modifier, value) => modifier ? _bem(namespace, block, "", "", modifier, value) : "";
  return {
    namespace,
    b,
    e,
    m,
  };
};

> packages/components/button/src/index.vue:template
<button :class="[ns.b(), ns.m('primary')]">这是一个测试的按钮</button>

useNamespace函数中定义e和m两个方法,分别是“元素”和“修改器”,两个方法均使用三元运算判断参数element或modifier然存在时才执行_bem函数获取类名(第7、9行)。button组件中便可调用useNamespace的b或m方法生成指定的类名(第19行),如图所示。

图片2.png

图4-7 useNamespace的m方法修改器生成类名 a-button--primary

useNamespace函数还可以定义其他的方法生成对应功能的类名,如loading加载、disabled禁用、active激活等,我们将在开发组件的过程中进一步完善useNamespace函数。