引言
好多小伙伴的css命名上比较头疼,今天我们来一起学习一下bem规范,并在Vue3项目中封装个函数用于返回bem规范的命名。
什么是bem
bem是block element modifier的缩写,b对应block(块),e对应element(元素),m对应modifier(修饰符)。
bem是由Yandex团队提出的一种前端命名css的命名规范。
- -中划线,作为连字符使用,用于某个块或者某个子元素的多单词间的连接记号。
- __ 双下划线:用于连接块和块之间的子元素。
- _ 单下划线 :用来描述一个块或者块的子元素的一种状态。
demo
我们来一起看一下bem的demo
el-form
el-input__inner
el-form--default
el-form-item
el-scrollbar__wrap--hidden-default
el-form-item--default
el-form-item__content--xxx
is-success或者is-required
实战
接下来我们在工程中封装一个hooks,目录下创建use-namespace
目录,use-namespace
目录下创建index,ts文件,先看下文件内容,文件内容如下:
import { computed, unref } from "vue";
// 默认命名前缀
export const defaultNamespace = "etu";
export const statePrefix = "is";
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;
};
export const useNamespace = (block: string) => {
// 命名前缀也就是命名空间
const namespace = computed(() => defaultNamespace);
// 创建块 例如:el-form
const b = (blockSuffix = "") =>
_bem(unref(namespace), block, blockSuffix, "", "");
// 创建元素 例如:el-input__inner
const e = (element?: string) =>
element ? _bem(unref(namespace), block, "", element, "") : "";
// 创建块修改器 例如:el-form--default
const m = (modifier?: string) =>
modifier ? _bem(unref(namespace), block, "", "", modifier) : "";
// 创建前缀块元素 例如:el-form-item
const be = (blockSuffix?: string, element?: string) =>
blockSuffix && element
? _bem(unref(namespace), block, blockSuffix, element, "")
: "";
// 创建元素修改器 例如:el-scrollbar__wrap--hidden-default
const em = (element?: string, modifier?: string) =>
element && modifier
? _bem(unref(namespace), block, "", element, modifier)
: "";
// 创建块前缀修改器 例如:el-form-item--default
const bm = (blockSuffix?: string, modifier?: string) =>
blockSuffix && modifier
? _bem(unref(namespace), block, blockSuffix, "", modifier)
: "";
// 创建块元素修改器 例如:el-form-item__content--xxx
const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
blockSuffix && element && modifier
? _bem(unref(namespace), block, blockSuffix, element, modifier)
: "";
// 创建动作状态 例如:is-success is-required
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}` : "";
};
return {
namespace,
b,
e,
m,
be,
em,
bm,
bem,
is,
};
};
接下来我们在使用的时候,比如需要创建一个形如el-menuGroup__mode,el-menuGroup-menuTrigger__bgColor--textColor的类,我们可以导入我们封装好的规范。
<script setup lang="ts" name="EtuMenuGroup">
import { computed } from "vue";
import { useNamespace } from "@etu-design/hooks";
const props = defineProps({
// 无关代码省略了...
});
const bem = useNamespace("menuGroup");
const tClass = computed(() => {
// el-form-item__content--xxx
return [
bem.e(props.mode),
bem.bem(props.menuTrigger, props.bgColor, props.textColor),
];
});
</script>
好处
bem有很多好处,除了命名规范易读,如果写scss的同学们可能会发现,bem的命名规范能完美的发挥scss的特性。
注意
bem不宜嵌套过深,一般推荐不超过四层。