首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

从 BEM 谈大型项目中 CSS 的组织和管理(1)

从 BEM 谈大型项目中 CSS 的组织和管理(1)

Web 应用发展到今天已经与传统的 Web 应用大不相同。依靠浏览器的支持以及前端技术和框架的发展,Web 应用可以执行强大的操作和提供丰富的交互性。很多应用已经把大量的逻辑从服务器端迁移到了浏览器端。服务器端通过 REST 服务暴露数据,浏览器端与用户进行交互来完成复杂的逻辑。由于这个发展趋势,Web 应用的前端代码的复杂度大大提高,尤其是 JavaScript 和 CSS 代码的数量大幅增加。对于大型 Web 应用来说,组织和管理如此大量的 JavaScript 和 CSS 代码是一个复杂的问题。本文主要讨论的是大型项目中 CSS 代码的组织方式。首先介绍的是前端项目开发方法学 BEM。
BEMBEM 是一种前端项目开发的方法学,由 Yandex 公司提出。BEM 的名称来源于该方法学的三个组成部分的英文首字母,分别是块(Block)、元素(Element)和修饰符(Modifier)。这三个不同的组成部分称为 BEM 实体。

块即是通常所说的 Web 应用开发中的组件或模块。每个块在逻辑上和功能上都是相互独立的。块中封装了组件相关的 JavaScript、CSS 代码和 HTML 模板。由于块是独立的,可以在应用开发中进行复用,从而降低代码重复并提高开发效率。块可以放置在页面上的任何位置,也可以互相嵌套。同一个块可以在页面上存在多个实例。块的不同实例具有相似的结构。一个典型的块的示例是菜单。一个项目中可以有多个不同的菜单,具体相似的结构和样式。
元素
元素是块中的组成部分。元素不能离开块来使用。BEM 不推荐在元素中嵌套其他元素。在菜单块中,每个菜单项是块中的元素。
修饰符
修饰符用来定义块或元素的外观和行为。同样的块在应用不同的修饰符之后,会有不同的外观。当菜单块出现在页面上的不同位置时,可以有不同的样式。菜单块中的菜单项可以有选中或非选中的状态。
不同 BEM 实体可以互相嵌套,形成 BEM 树。不过 BEM 树和 HTML 页面中的 DOM 树是不同的。BEM 树的抽象层次更高。在实现中需要避免在两者之间建立硬性的对应关系。
CSS 命名规则具体到 CSS 来说,BEM 的重要内容之一是其引入的 CSS 命名规范。一个良好的命名规范可以帮助开发人员快速的了解 CSS 代码中不同样式规则的含义。这对于团队协作和代码维护有很大好处。BEM 有自己独特的 CSS 命名规则,与 BEM 中的三种实体相对应。
首先每个 BEM 实体都有一个名称,由小写字母和数字组成。名称中的不同单词通过连字符(-)连接,如 property-editor 或 language-switcher 等。每个 CSS 类别选择器的名称包含了其所描述的元素的 BEM 相关的信息。因此从每个元素上的 class 属性的值就可以知道该元素的 BEM 信息,从而了解元素在组件中的作用。
BEM 中的 CSS 类名的第一部分是块的名称。块的名称作为 CSS 类名的命名空间。如表示菜单的块的名称可以是 menu。该块中的元素的 CSS 类名都会包含 menu 作为前缀。与块中元素对应的 CSS 类名是在块的名称后加上元素的名称,并通过两个下划线(__)分隔。如菜单块中的每个菜单项的名称是 item,则与之对应的 CSS 类名是 menu__item。修饰符可以用来修饰块或元素。在 CSS 类名中,修饰符紧跟在所修饰的实体的名称之后,并通过单个下划线(_)分隔。有两种类型的修饰符,分别是布尔修饰符和名值对修饰符。布尔修饰符直接出现在对应的实体的名称之后,表示某种状态。如当前选中的菜单项对应的 CSS 类名是 menu__item_selected,其中 selected 是布尔修饰符。名值对修饰符由名称和值两部分组成,通过单个下划线(_)分隔。比如表示订单的块 order,可以通过名称为 status 的修饰符表示订单的不同状态,如 CSS 类名 order_status_paid 对应 status 为 paid 的订单。需要注意的是在一个 CSS 类名中不应该同时出现块和元素的修饰符。
上述给出的是 BEM 方法学提出者 Yandex 的命名规则,社区中还有其他不同的命名规则,区别在于 BEM 实体名称的命名规则以及 CSS 类名中不同部分的分隔符。比如有的命名规则使用 camel case 来命名 BEM 实体,如 PropertyEditor 和 LanguageSwitcher;有的命名规则使用两个连字符(--)来分隔修饰符,如 menu__item--selected。目前来说,比较流行的是由 Harry Roberts 提出的命名规范,也是 Google 的 Material Design Lite 库使用的命名规则。本文中的代码也使用该规则。该命名规则的原则如下:
  • BEM 实体名称全部是小写字母或数字。名称中的不同单词用单个连字符(-)分隔。
  • BEM 元素名称和块名称之间通过两个下划线(__)分隔。
  • 布尔修饰符和其所修饰的实体名称之间通过两个连字符(--)来分隔。不使用名值对修饰符。
通过使用上面 CSS 命名规则,所得到的是一系列只使用单个 CSS 类别选择器的样式规则。以菜单组件作为示例来说明。  和  中给出了使用 BEM 命名规则的 HTML 代码和 CSS 代码。示例中包含了 BEM 块 menu、元素 item 和修饰符 selected。
清单 1. 使用 BEM 命名规则的 CSS 类的 HTML 代码
1
2
3
4
5
<ul class="menu">
<li class="menu__item menu__item--selected">Menu Item 1</li>
<li class="menu__item">Menu Item 2</li>
<li class="menu__item">Menu Item 3</li>
</ul>




清单 2. 使用 BEM 命名规则的 CSS 样式表
1
2
3
4
5
6
7
8
9
.menu {
list-style: none;
}
.menu__item {
font-weight: bold;
}
.menu__item--selected {
color: red;
}




  和  中给出的是另外一种 CSS 代码的组织方式。
清单 3. 使用传统命名规则的 CSS 类的 HTML 代码
1
2
3
4
5
<ul class="menu">
<li class="item selected">Menu Item 1</li>
<li class="item">Menu Item 2</li>
<li class="item">Menu Item 3</li>
</ul>




清单 4. 使用传统命名规则的 CSS 样式表
1
2
3
4
5
6
7
8
9
10
11
.menu {
list-style: none;
}
  
.menu .item {
font-weight: bold;
}
  
.menu .item.selected {
color: red;
}




比较这两种不同的方式,BEM 的优点在于所产生的 CSS 类名都只使用一个类别选择器,可以避免传统做法中由于多个类别选择器嵌套带来的复杂的属性级联问题。在 BEM 命名规则中,所有的 CSS 样式规则都只用一个类别选择器。因此所有样式规则的特异性(specificity)都是相同的,也就不存在复杂的优先级问题。这可以简化属性值的层叠规则。代码清单中的命名规则的好处在于每个 CSS 类名都很简单明了,而且类名的层次关系可以与 DOM 节点的树型结构相对应。BEM 命名规则的可能的缺点在于 CSS 类名会比较长而且复杂。乍看之下,根据 BEM 命名规则产生的 CSS 类名都会很复杂,但实际上在熟悉了命名规则之后,可以很容易理解其含义。
返回列表