Write More Maintainable CSS with BEM

如何用 BEM 撰写更好维护的 CSS

为什么需要 BEM ?

BEM 是一种管理 CSS 编写方式的规范。撰写小型的网站时通常不需要特别考虑到未来样式的命名与权级的规划,但随着网站的复杂度增长就必须需要一套可预测与可扩展的方式,BEM CSS🔗 由于概念简洁好懂,成效显著,并且有着相对长远的历史被测试与应用过,很适合作为初学者第一套管理 CSS 的办法。

学习 BEM 能带来哪些好处?

  1. 更好的 描述网页组件的功能
  2. 更好的 交代网页组件的架构
  3. 更好的 避免选取权重问题

开始学习 BEM 之前……

开发者应当理解「语义化命名」与「模块化」撰写网页的重要性,才能在此基础上使用 BEM 更进一步的分类管理样式。

语义化命名

<!-- 不正确,red-text 仅描述组件的外貌 -->
<div class="red-text"></div>
<!-- 正确,error 有意义的描述组件 -->
<div class="error"></div>

所有的 BEM CSS 都应该以语义化的方式来命名,如:button, form, header,而非无意义的命名或仅描述外貌与结果的命名,如:aaa, red, center,语义化的命名可以帮助开发者描述组件的用途与关系,更好地撰写出可长期维护与扩展的代码。

模块化

<!-- 不正确,无法重复利用的样式 -->
<a href="#" class="button">
<a href="#" class="button-solid">
<a href="#" class="button-outlined">
<!-- 正确,拆分重复度高的样式赋予名称统一管理 -->
<a href="#" class="button">
<a href="#" class="button button-solid">
<a href="#" class="button button-outlined">

网页可以被视为一块块的组件 (Component),将高度重复的样式给抽离出来。举例来说:如果开发上有许多种类的按钮,应当制作一个 buttonclass ,并依照样式功能上的不同再添加 class ,像是:实心(solid)、空心(outlined)、危险(danger)、警告(warning) 而非为每个种类的按钮都撰写一个无法重复利用的 class

开始学习 BEM

BEM 将网页上的任何组件看作是一块块功能独立、可以被重复利用的区块,并运用元素来标注区块不可分割的子项目,或运用修饰符创造不同功能样式的区块。听起来非常复杂?让我们进入下一个章节详细了解这三种分类的差别与用途:

  • B - 区块 (Block)
  • E - 元素 (Element)
  • M - 修饰符 (Modifier)

区块 Block

撰写 BEM 先从区块开始,实际上就和一般专写 CSS 时没有两样,要注意区块应当避免影响环境,不同区块之间不应该相互影响且区块之间是对等的,并没有一定的从属关系。举例来说:logo 常常出现在网站的 navbar 中,但不代表一定只能在 navbar 之中,这就是「没有一定从属关系」的意思,如果有从属关系应当归类到 元素 之中。

<!-- header 区块 -->
<header class="header">
<!-- 嵌套的 logo 区块 -->
<div class="logo"></div>
<!-- 嵌套的 search-form 区块 -->
<form class="searchForm"></form>
</header>
<footer class="footer">
<!-- logo 仍可以在 footer 中出现 -->
<div class="logo"></div>
</footer>
  • 区块之间并没有从属关系。
  • 区块不应影响环境,举例来说:不使用 marginposition 。确保区块可以被移动并且重复使用。
  • 区块可以被嵌套在一块,多少层次都是可行的。

元素 Element

元素无法独立存在,且会隶属于一个区块,举例来说:navbar 是一个区块,但内部的 lightSwitch 却只能用在 navbar 中,因此将它定义为 navbar 的元素 navbar__lightSwitch,这个 navbar__lightSwitch 并不会在 navbar 以外的任何地方出现,这就是元素。

<!-- navbar 区块 -->
<nav class="navbar">
<!-- navbar内的 lightSwitch 元素 -->
<button class="navbar__lightSwitch">light</button>
</nav>
  • 元素需在区块后命名加上两个底线(__)来表示。
  • 元素永远隶属于某个区块中,无法被单独使用。
  • 元素可以被嵌套在一块,多少层次都是可行的。
  • 元素无法被双重叠加,举例:block__elem1__elem2

修饰符 Modifier

举例来说,一个按钮它的外貌特征(大小 / 风格),或是状态(启用 / 隐藏) 都可以使用修饰符附注上去。

  • 修饰符需加上两个连接号(–-)来表示
  • 修饰符仅描述区块或元素被附加的样式,无法被单独使用。
<!-- 是 focused 状态的 searchForm 区块 -->
<button class="button button--large button--active"></button>

BEM 常见的问题

可以使用进阶选择器吗?

不行。 BEM 只使用 class 选择器就是为了迴避选择权级越叠越高导致难管理的问题。

好像每个人的 BEM 都长得不太一样?

BEM 的核心概念 Block/Element/Modifier 要怎么撰写与区分它们,就依照个人或开发团队喜好去改变了。本篇文章所有范例都添加了笔者的个人偏好(2 Dashes style🔗 + Camel Case Style🔗),可以参考 BEM Naming convention🔗 来了解各种范例。

我应该使用 BEM 吗?

如果专案并没有这么大,只是随手写个 CSS 那么可能并不需要 BEM。BEM 最被人诟病的一点,就是过长过复杂的 class 名称,以及失去使用各种选择器的便捷性。其他如果是 Utility-First CSS 优先的开发模式的话,那么从根本上就不需要 BEM ,不过这又是另一个篇章的话题了。

参考资料