编写组件的时候如果通过手写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所示。
图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行),如图所示。
图4-7 useNamespace的m方法修改器生成类名 a-button--primary
useNamespace函数还可以定义其他的方法生成对应功能的类名,如loading加载、disabled禁用、active激活等,我们将在开发组件的过程中进一步完善useNamespace函数。