文章目录[隐藏]
最近在做新的博客主题,又恰巧在整理 HTML 中为元素命名方面的心得,就借机重温了一下 BEM 文档。或许是太久没看官方文档,我发现 BEM 如今已经延伸到了 Web 开发实践的范畴,而不再是一个单纯的命名规范。好坏不谈,总结一下注意事项。
命名规则
过去的 BEM 语法冗长丑陋,期间也经历了几种方式(现在依然共存):
/* 旧:破折号式 */
.block-name {}
.block-name__element-name {}
.block-name__element-name--modifier-name {}
/* 旧:驼峰式 */
.blockName {}
.blockName--modifierName {}
.blockName__elementName--modifierName {}
而现在的命名规则主要是对修饰符的连接符做了调整——破折号改为单下划线链接:
.block-name {}
.block-name__element-name_modifier-name {}
如何使用块与元素
- 如果一个区域可以复用且不依赖其它组件,则可作为一个块(Block)。
- 如果一个区域不能拿到外部单独使用,那么就应该作为一个元素(Element)。
- 元素不能单独使用,只能作为块的一部分使用。元素不能包含元素,因为这样做会妨碍块内部元素位置的层级调整。
<!-- 错误范例 --> <form class="search-form"> <div class="search-form__content"> <!-- 应该使用: search-form__input 或 search-form__content-input --> <input class="search-form__content__input"> <!-- 应该使用:search-form__button 或 search-form__content-button --> <button class="search-form__content__button">搜索</button> </div> </form>
- 如果一个元素可以继续分解,则可使用「元素 + 块」混合的方式另起一个空间,下面会单独介绍。
修饰符的改进
修饰符(Modifier)用来修饰块或元素的外表、状态或行为。修饰符不能单独使用,而且必须绑定在对应的块或元素上,不能混搭。
<button class="button button_active">...</button>
现在除了布尔值修饰符,还可以使用 Key-value 形式。
- 一般来讲,如果修饰符的值可有可无(disabled、readonly 等,有则是 true,没有则是 false),那么可以省略修饰符的值。
- 如果修饰符的值可以有多种状态,则使用 Key-value 形式。
另外,因为修饰符的连接符从 --
变成了 _
,所以并不像过去那么丑陋了。
/* 使用布尔值表示状态(禁用、可见、聚焦等) */
.btn_disabled {}
.header__search-form_focused {}
/* 使用 Key-value 表示选项(尺寸、主题、类型等) */
.btn_size_s {}
.search-form_theme_dark {}
.menu__item_type_text {}
为什么修饰符一定要包含块名或元素名?
- 每一个块都会建立独立的命名空间,可以有效的减少命名冲突;
- 显式说明修饰符所对应的块或元素,防止混淆;
- 代码搜索更加准确方便;
- 不包含块名或元素名,使用修饰符就必须使用组合选择器(
.btn.disabled
),这会增加样式权重使其难以覆盖。
使用混合拆分样式
在 BEM 中,位置和布局样式通过父级块来进行设置。这就需要通过混合(Mix)组合块与元素,组合多个实体(块、元素、修饰符都被称作 BEM 实体)的表现与样式,同时不耦合代码。
<!-- header 块 -->
<div class="header">
<!-- `search-form` 块混合 header 块的 `search-form` 元素 -->
<div class="search-form header__search-form"></div>
</div>
上面的例子通过混合的方式把位置样式从块中剥离,然后就可以在 .header__search-form {}
中设置表单的位置或浮动等样式,从而保持了 search-form
块的样式独立,对其完整样式代码进行了解耦。这并不陌生——在传统命名方式中,我们经常通过嵌套的方式(.header .search-form
)对局部样式进行调整。但这样做会改变选择器的权重。在 BEM 的思想中,保持选择器扁平和低权重是一个准则。
因此,在使用 BEM 时需要格外注意遵循它的工作方式:
- 不在块里设置位置、布局相关的样式,只设置基本样式;
- 通过混合的方式,在作为父级块的元素时设置布局样式;
- 适时拆分元素为独立的块,解耦样式并形成新的命名空间。
避免组合使用标签与类选择器
在 CSS 中使用 tag 一直不是一个好的实践,但在有些时候却难以避免。唯一需要注意的是 a.btn
是无法被 .btn_active
覆盖的。因此组合使用标签与类选择器时要格外注意。当然,最好还是避免使用。
适时使用嵌套选择器
很多人说 BEM 是「禁止嵌套」的,这不完全准确。BEM 只是建议尽量保持嵌套层级越低越好。因为 CSS 的权重问题很多人处理不好,最终恶果就是不停的嵌套和为了增加权重而进行的无意义嵌套和 !important
,这无疑增加了代码的耦合。
在你需要通过块状态对内部元素进行调整时,使用嵌套是很好的选择:
.btn_active .btn__text {}
.search-form_foucsed .search-form__input {}
.nav_theme_dark .nav__item {}
块不一定是一个视觉上的区块
在批量设置多个样式时,可以使用混合的方式建立一个纯样式抽象的「块」。此时块不对应页面上具体的一块区域,而是代表一组抽象样式。
<article class="article text"></article>
<footer class="footer">
<div class="copyright text"></div>
</footer>
上面的例子把 .text
作为一个单独的文本样式块,用来声明特定的一种文字设置。
.text {
font-family: Arial, sans-serif;
font-size: 14px;
color: #000;
}
同理,页面整体布局、栅格系统等,都可以作为单独的块来设置。
开放/关闭原则
任何 HTML 元素都应遵循向修饰符开放并拒绝改变的原则。换句话说,就是一个 HTML 元素应该便于通过修饰符对齐进行样式覆盖,但是不应该频繁对其本身进行修改,因为每次改动都将影响到所有元素。
切换到 BEM 式 CSS
- 把 DOM 模型扔到一边,学习创造块。
- 不要使用 ID 选择器和标签选择器。
- 最小化选择器嵌套。
- 使用 class 命名约定来避免命名冲突,确保选择器名称具备自解释性。
- 用块、元素和修饰符的方式工作。
- 把可能会改变的 CSS 样式属性从块挪到修饰符里去。
- 使用混合。
- 把代码拆分成独立的部分从而更容易的使用单独的块。
- 重复使用块。
总结
使用 BEM 的好处有很多:
- 自解释性很强,看到即看懂。
- 强制 「块--元素_修饰符」结构,命名重复可能性大大降低,有效地防止了样式污染问题。
- 抽象核心样式,通过修饰符的方式减少代码复制。
- 提供良好的扩展性。
而且现在 BEM 除了针对 CSS 的方案,在 JavaScript、文件解构上也有了充分设计。按照官方的姿态,BEM 已经不应再作为 SMACSS 或 OOCSS 的类比,而是接近 Web Components 的产品,同时提供了完整的针对项目的构造方案。
我个人还是更喜欢把它当做一个单纯的 CSS 命名方案来看待,因为在组件化开发的方面,现在有很多更好的方案可供选择。另外 BEM 也不是万能灵药,它虽然解决了命名冲突、权重混乱等问题,但在模块组织上并不是特别清晰,借鉴一些 SMACSS 中的思想会让你的项目更加清晰有条理。
光说不练假把式,在接下来的新主题中就会用 BEM + SMACSS 来操练一番。
-- EOF --