BEM命名规范
在前端开发中,随着项目规模的扩大,CSS代码的组织和维护变得越来越具有挑战性。开发者常常面临类名冲突、样式覆盖、代码难以维护等问题。为了解决这些问题,各种CSS命名规范和方法论应运而生,而BEM(Block-Element-Modifier)作为其中最流行和实用的方法之一,被越来越多的开发团队所采用。
1、什么是BEM规范
BEM全称:“Block Element Modifier”,意思是:“块、元素、修饰符”,意思是任意CSS命名都可以按照上述三类进行命名。
但是除了上述块、元素、修饰符以外,其实还有一类“State”表示“状态”,所以我更愿意叫“BEMS”而不是“BEM”。
2、BEM的三个核心概念
2.1 Block(块)
Block是一个独立的组件,可以在项目中重复使用,且不依赖于页面上的其他组件。
- 命名规则:使用小写字母,多个单词之间用连字符(-)连接。
示例:
.header {}
.menu {}
.search-form {}
2.2 Element(元素)
Element是Block的组成部分,不能脱离Block单独使用。
- 命名规则:Block名称 + 双下划线(__)+ Element名称。
示例:
.menu__item {}
.search-form__input {}
.header__logo {}
按钮文字(btn__text)
卡片标题(card__title)
2.3 Modifier(修饰符)
Modifier用于定义Block或Element的外观、状态或行为。
命名规则:
- 对于Block的修饰:Block名称 + 双连字符(
--)+ Modifier名称 - 对于Element的修饰:Block名称__Element名称 + 双连字符(
--)+ Modifier名称
示例:
/* Block修饰符 */
.menu--fixed {}
.button--large {}
/* Element修饰符 */
.menu__item--active {}
.search-form__input--disabled {}
按钮尺寸(btn--large)
卡片标题颜色(card__title--warning)
2.4 State状态
- 命名符:“is”、“has”、“-”(短横线)
- 命名格式:is-state / has-property
- 作用:通用状态类,允许跨块复用
- 示例场景:禁用状态(is-disabled)、含有未读属性(has-unread)
3、为什么要用BEM
使用BEM无非就是两点:
- BEM命名更加工整,他人阅读代码更加方便,一目了然
- 在某些情况下(如vue组件封装),可以防止作用域冲突
对于第一点就不再多说,着重说明第二点,我们假设有“App.vue”和“child.vue”两个vue文件,其中App.vue是父组件,child.vue是子组件,具体代码如下:
App.vue:
<script setup>
import child from './views/child.vue';
</script>
<template>
<div class="container">
<div class="title">
<span>这是父组件区域</span>
</div>
<div class="content">
<child />
</div>
</div>
</template>
<style scoped>
.container {
width: 100%;
height: 100%;
color: red;
background-color: blueviolet;
}
.container .title {
font-size: 20px;
font-weight: bold;
color: black;
}
</style>
child.vue:
<script setup>
</script>
<template>
<div class="container">
<div class="title">这是子组件区域</div>
</div>
</template>
<style scoped>
.container {
background-color: white;
color: green;
}
.container .title {
color: orange;
}
</style>
效果:

此时子组件的container明明是白色的背景为什么还会是紫色的呢?
我们打开F12开发者工具发现:


子组件的container居然被父组件的container顶掉了,官方给出的结论是:
- 子组件的根元素会继承父组件的所有样式
为此,使用BEM命名可以有效的避免子组件的CSS与父组件的CSS重名。
4、BEM示例
4.1 基本示例
以一个简单的导航菜单为例:
<nav class="nav">
<ul class="nav__list">
<li class="nav__item">
<a href="#" class="nav__link nav__link--active">首页</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">关于我们</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">服务</a>
</li>
<li class="nav__item">
<a href="#" class="nav__link">联系我们</a>
</li>
</ul>
</nav>
对应的CSS:
.nav {
background-color: #333;
padding: 10px;
}
.nav__list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.nav__item {
margin-right: 20px;
}
.nav__link {
color: white;
text-decoration: none;
}
.nav__link--active {
font-weight: bold;
border-bottom: 2px solid white;
}
4.2 复杂示例
对于更复杂的组件,如一个带有标题、内容和操作按钮的卡片:
<div class="card">
<div class="card__header">
<h2 class="card__title">文章标题</h2>
<span class="card__date">2023-05-15</span>
</div>
<div class="card__content">
<p class="card__text">这是文章的内容...</p>
<img class="card__image" src="image.jpg" alt="图片描述">
</div>
<div class="card__footer">
<button class="card__button card__button--primary">阅读更多</button>
<button class="card__button card__button--secondary">分享</button>
</div>
</div>
对应的CSS:
.card {
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
margin-bottom: 20px;
}
.card__header {
padding: 15px;
background-color: #f5f5f5;
display: flex;
justify-content: space-between;
align-items: center;
}
.card__title {
margin: 0;
font-size: 18px;
}
.card__date {
color: #666;
font-size: 14px;
}
.card__content {
padding: 15px;
}
.card__text {
margin-top: 0;
}
.card__image {
max-width: 100%;
height: auto;
margin-top: 10px;
}
.card__footer {
padding: 15px;
background-color: #f5f5f5;
display: flex;
justify-content: flex-end;
}
.card__button {
padding: 8px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-left: 10px;
}
.card__button--primary {
background-color: #007bff;
color: white;
}
.card__button--secondary {
background-color: #6c757d;
color: white;
}
5、BEM的常见问题与解决方案
5.1 类名过长
当项目结构复杂时,BEM类名可能会变得很长,影响代码的可读性。
解决方案:
- 使用简短但有意义的Block和Element名称
- 考虑使用预处理器(如SASS或LESS)的嵌套功能来简化编写
- 在SASS/SCSS中,&符号表示父选择器的引用,可以大大简化BEM风格的CSS编写:
.card {
&__header { /* .card__header */ }
&__title { /* .card__title */ }
&__button {
/* .card__button */
&--primary { /* .card__button--primary */ }
&--secondary { /* .card__button--secondary */ }
}
}
5.2 嵌套元素的命名
当元素嵌套层级较深时,可能会出现命名困难的情况。
解决方案:BEM不推荐反映DOM结构的类名,如.block__elem1__elem2。应该始终保持扁平的命名结构:.block__elem1和.block__elem2。
<!-- 不推荐 -->
<div class="card">
<div class="card__content">
<div class="card__content__section">...</div>
</div>
</div>
<!-- 推荐 -->
<div class="card">
<div class="card__content">
<div class="card__section">...</div>
</div>
</div>
5.3 何时创建新的Block
有时候很难判断应该创建一个新的Block还是继续使用Element。
判断标准:如果一个组件可以在不同的上下文中独立使用,那么它应该是一个Block;如果它只在特定Block中使用,那么它应该是一个Element。
6、总结
BEM命名规范是一种强大的CSS组织方法,它通过明确的命名约定,帮助开发者创建模块化、可维护的CSS代码。虽然在某些情况下类名可能会变得冗长,但其带来的代码清晰度和维护性的提升远远超过了这一小缺点。无论是小型项目还是大型应用,采用BEM命名规范都能帮助你构建更加健壮和可扩展的前端代码结构。

325

被折叠的 条评论
为什么被折叠?



