本文概述
什么是BEM方法论?
当你构建较小的网站时, 如何组织样式通常不是大问题。你创建了通常的文件, 编写了所有必需的CSS, 仅此而已。但是, 当涉及到更大, 更复杂的项目时, 如何组织代码就变得至关重要。如果你在由多个前端和后端开发人员组成的团队中工作, 则代码的结构尤为重要。
BEM方法论将大大提高代码的可维护性并加快开发过程
鸣叫
如今, 有许多方法可以减少CSS代码并提高CSS代码的可维护性。在本文中, 我将解释并提供其中一个示例:BEM。 BEM代表块元素修改器。其背后的主要思想是通过将CSS类安排到独立模块中来加快开发过程并减轻开发人员的团队合作。如果你看到过诸如header__form–search之类的类名, 那么这就是实际中的BEM。是的, 类可以被命名很长, 但是它们都是可读和可理解的。
请注意, 最佳实践是仅对类使用BEM, 而对ID不使用BEM, 因为类允许你在必要时重复名称并创建更一致的编码结构。另外, 如果你想将网站分成有组织的模块, 则它应该由相同的结构组成:块, 元素和修饰符。每个块可以有多个元素, 而块和元素都可以有多个修饰符。但是, 让我们首先从基本的BEM结构开始, 并通过示例进行解释。
块
块代表你网站中的一个对象。将其视为代码的更大结构块。今天, 每个网站上最常见的块是标题, 内容, 侧边栏, 页脚和搜索。 BEM中的块始终是链接CSS类的起点。看几个示例:
- 内容
- 一份菜单
- 搜索表格
.content {/* Styles */}
.menu {/* Styles */}
.search {/* Styles */}
元件
元素是块中执行特定功能的组件。它仅在其块上下文中才有意义:
- 内容文章
- 菜单项
- 搜索输入字段
.content__article {/* Styles */}
.menu__item {/* Styles */}
.search__input {/* Styles */}
编辑
修饰符是我们如何表示块的变体。如果你曾经使用过Bootstrap, 那么最好的例子就是按钮的尺寸。按钮大小只是按钮本身的大小变化, 这使其成为修饰符:
- 内容精选文章
- 菜单链接
- 带有或不带有图标的搜索字段
.content__article--featured {/* Styles */}
.menu__item--link {/* Styles */}
.search__input--icon {/* Styles */}
命名约定
BEM方法的主要目的是使CSS选择器的名称尽可能具有信息性和透明性。原始BEM样式是通过以下方式定义的:
块名通常是一个单词, 例如.header, 但是如果你有更长的块定义, 那么它会用单个连字符-分隔:
.lang-switcher {/* Styles */}
元素名称以双下划线__开头:
.lang-switcher__flag {/* Styles */}
修饰符名称以单个下划线_开头:
.lang-switcher__flag_basic {/* Styles */}
BEM方法中只有一个非常关键的规则-修饰符不能在其所有者的上下文之外使用。
例子:
.btn_big {/* Styles */}
仅当还定义了标头时, 才可以使用btn_big。
错误的例子:
<div class="btn_big">...</div>
好的例子:
<div class="btn btn_big">...</div>
除了这些原始的BEM样式外, 还有其他命名方案, 例如Harry Roberts和CamelCase样式。
哈里·罗伯茨(Harry Roberts)风格的例子:
.block-name__element-name--modifier-name {/* Styles */}
CamelCase样式示例:
.BlockName__ElementName_ModifierName {/* Styles */}
其他的也很少, 但是这两个是最常见的。我个人是哈里斯·罗伯茨(Harris Roberts)提出的命名约定的粉丝, 该命名约定具有以下规则:
- 名称用小写
- BEM实体名称中的单词由连字符分隔-
- 元素名称与块名称之间用双下划线__隔开
- 布尔修饰符由双连字符分隔-
- 不使用键值类型修饰符
这种命名约定比其他约定好得多的原因是, 你可以轻松地将修饰符元素与其他元素区分开。在原始命名约定中, 修饰符的定义如下:
.block__element_modifier {/* Styles */}
但是正如你所看到的, 单下划线和双下划线之间并没有太大的区别。另一方面, 双连字符提供了清晰的分隔, 你可以立即看到修饰符:
.block__element--modifier {/* Styles */}
不同格式的BEM示例
请注意, 除了CSS, BEM在组织JSON, XML, 树文件或任何支持嵌套的格式中也非常有用。将BEM方法学视为构建UI的好方法。
让我们考虑以下BEM格式的HTML:
<header class="header">
<img class="header__logo">
<form class="header__search-from">
<input class="header__search-from__input" type="input">
<button class="header__search-from__button" type="button">
</form>
<div class="header__lang-switcher"></div>
</header>
使用JSON和XML格式可以实现相同的目的。
XML:
<block:header>
<block:logo/>
<block:search-from>
<block:input/>
<block:button/>
</block>
<block:lang-switcher/>
</block>
JSON:
{
block: ‘header’, content: [
{ block: ‘logo’ }, {
block: ‘search-form’, content: [
{ block: ‘input’ }, { block: ‘button’ }
]
}, { block: ‘lang-switcher’ }
]
}
BEM项目的文件系统组织
在BEM中, 以正确的方式组织文件至关重要。 BEM不仅为你提供了很好的CSS类组织并使它们完全可理解, 而且还为你提供了一个非常可维护的文件结构。让我们以BEM文件组织技术和SASS文件为例:
blocks/
input/
__box/
--big/
input__box--big.scss
input__box.scss
button/
--big/
button--big.scss
正如你在上面看到的, 仅通过查看主文件夹中的子文件夹结构, 所有内容都是清晰且井井有条的。这样, 谁跟随你工作或你跟随某人工作都没有区别, 因为遵循相同的模式非常容易。
将BEM项目划分为平台
除了仅使用BEM方法学技术来组织文件之外, 你还可以进行更具体的事情。例如, 如果你要构建一个将完全响应的Web项目, 并且客户端指定移动设备上的某些块与桌面设备上的某些块完全不同, 则最好将BEM文件夹结构划分为平台。在各种平台上组织按钮的示例:
common.blocks/
button/
button.scss
desktop.blocks/
button/
buttons.scss
mobile.blocks/
button/
button.scss
请注意, 这只是一个示例, 如果你想使用BEM方法来组织整个项目。具有BEM结构的文件树不是强制使用BEM的强制性要求, 你可以仅在项目的某些部分中使用BEM。到目前为止, 我还没有使用这个严格的BEM文件结构组织来创建每个元素和修饰符的文件。相反, 我只是为具有其元素和修饰符声明的块创建文件结构。
实践中的BEM
由于你现在熟悉命名约定, 因此我将在实践中演示BEM方法。假设我们正在执行以下HTML代码:
<a class="btn btn--big btn--primary-color" href="#" title="Title">
<span class="btn__price">$3.99</span>
<span class="btn__text">Product</span>
</a>
应用以下CSS标记:
.btn__price {/* Styles */}
.btn__text {/* Styles */}
.btn--big {/* Styles */}
.btn--primary-color {/* Styles */}
现在, 不要误导。到目前为止, 在我们的示例中, 我们几乎总是有一个块, 元素和修饰符, 而并非总是如此。
例如, 假设我们有一个名为person的街区。一个人有腿和手, 也可以是女性或男性。如果我们想用右手定义一个男性, 它将看起来像这样:
.person--male__hand--right {/* Styles */}
现在你可以看到BEM的真正含义。我们定义了一个修饰语为性别的人。由于一个人是男性还是女性都没有关系, 所以它有一只手, 而手是其中的一环。同样, 每个人都可以有右手或左手, 这又是一个修饰符。
在另一种情况下, 如果我们想用一只手来定义普通人, 我们将这样做:
.person__hand {/* Styles */}
如你所见, 一旦你对BEM感到满意, 便可以轻松地构建CSS和HTML结构。
将BEM与CSS预处理程序一起使用
就个人而言, 我无法想象不使用CSS预处理程序之一就可以启动任何新项目。众所周知, 预处理器是一件了不起的事情, 它们为我们提供了很多好处, 最重要的是, 它们与BEM方法学完美匹配。
相关:拥抱Sass:为什么应该停止使用香草CSS
在以下示例中, 你可以看到与SASS结合使用的BEM最典型示例:
.person {
&__hand {/* Styles */}
&__leg {/* Styles */}
&--male {
/* Styles */
&__hand {
/* Styles */
&--left {/* Styles */}
&--right {/* Styles */}
}
&__leg {
/* Styles */
&--left {/* Styles */}
&--right {/* Styles */}
}
}
&--female {
/* Styles */
&__hand {
/* Styles */
&--left {/* Styles */}
&--right {/* Styles */}
}
&__leg {
/* Styles */
&--left {/* Styles */}
&--right {/* Styles */}
}
}
}
SASS代码将编译为以下CSS:
.person__hand {/* Styles */}
.person__leg {/* Styles */}
.person--male {/* Styles */}
.person--male__hand {/* Styles */}
.person--male__hand--left {/* Styles */}
.person--male__hand--right {/* Styles */}
.person--male__leg {/* Styles */}
.person--male__leg--left {/* Styles */}
.person--male__leg--right {/* Styles */}
.person--female {/* Styles */}
.person--female__hand {/* Styles */}
.person--female__hand--left {/* Styles */}
.person--female__hand--right {/* Styles */}
.person--female__leg {/* Styles */}
.person--female__leg--left {/* Styles */}
.person--female__leg--right {/* Styles */}
如果你想走得更远, 可以将便捷的SASS mixins用于BEM:
/// Block Element
/// @param {String} $element - Element's name
@mixin element($element) {
&__#{$element} {
@content;
}
}
/// Block Modifier
/// @param {String} $modifier - Modifier's name
@mixin modifier($modifier) {
&--#{$modifier} {
@content;
}
}
你可以像这样使用它:
.person {
@include element('hand') {/* Person hand */}
@include element('leg') {/* Person leg */}
@include modifier('male') {
/* Person male */
@include element('hand') {
/* Person male hand */
@include modifier('left') {
/* Person male left hand */
}
@include modifier('right') {
/* Person male right hand */
}
}
}
}
它将产生以下CSS输出:
.person__hand {
/* Person hand */
}
.person__leg {
/* Person leg */
}
.person--male {
/* Person male */
}
.person--male__hand {
/* Person male hand */
}
.person--male__hand--left {
/* Person male left hand */
}
.person--male__hand--right {
/* Person male right hand */
}
我知道你很可能不会有这么长时间的用例, 但这是一个很好的例子, 说明了在小型和大型项目中BEM的使用方式以及其如此强大的功能。
开始你的BEM项目
如官方的BEM文档中所述, 开始拥有新的BEM项目的最简单方法是使用现有的GIT存储库。只需使用Git clone命令:
$ git clone https://github.com/bem/project-stub.git
接下来, 转到新创建的目录并安装所有依赖项:
$ npm install
将安装所有必需的依赖项:
使用ENB构建项目:
$ node_modules/.bin/enb make
运行服务器模式进行开发:
$ node_modules/.bin/enb server
结果, 出现以下消息:
Server started at 0.0.0.0:8080
现在, 这意味着服务器已启动并正在运行。现在, 你可以在以下地址检查结果:
http://localhost:8080/desktop.bundles/index/index.html
如你所见, 已经创建了很多元素, 这些元素在bemjson文件中定义, 该文件位于以下位置:
project-stub/desktop.bundles/index/index.bemjson.js
你可以查看并浏览生成所有HTML的文件的当前结构, 你可以在localhost index.html文件中看到该结构。我们将更改此文件以获得上一章中介绍的”人” BEM项目。你可以从index.bemjson.js文件中删除(或注释)整个代码, 并将其替换为以下代码:
module.exports = {
block: 'page', title: 'Person BEM', favicon : '/favicon.ico', head : [
{ elem : 'meta', attrs : { name : 'description', content : '' } }, { elem : 'meta', attrs : { name : 'viewport', content : 'width=device-width, initial-scale=1' } }, { elem : 'css', url : 'index.min.css' }
], scripts: [{ elem : 'js', url : 'index.min.js' }], content: [
{
block: 'person', content: [
{
elem: 'male', content: [
{
elem: 'leg', mods: {side: 'left'}, content: 'Male person leg -- left'
}, {
elem: 'leg', mods: {side: 'right'}, content: 'Male person leg -- right'
}, {
elem: 'hand', mods: {side: 'left'}, content: 'Male person hand -- left'
}, {
elem: 'hand', mods: {side: 'right'}, content: 'Male person hand -- right'
}
]
}, {
elem: 'female', content: [
{
elem: 'leg', mods: {side: 'left'}, content: 'Female person leg -- left'
}, {
elem: 'leg', mods: {side: 'right'}, content: 'Female person leg -- right'
}, {
elem: 'hand', mods: {side: 'left'}, content: 'Female person hand -- left'
}, {
elem: 'hand', mods: {side: 'right'}, content: 'Female person hand -- right'
}
]
}, ]
}
]
};
现在, 将生成以下HTML:
<div class="person">
<div class="person__male">
<div class="person__leg person__leg_side_left">
Male person leg -- left
</div>
<div class="person__leg person__leg_side_right">
Male person leg -- right
</div>
<div class="person__hand person__hand_side_left">
Male person hand -- left
</div>
<div class="person__hand person__hand_side_right">
Male person hand -- right
</div>
</div>
<div class="person__female">
<div class="person__leg person__leg_side_left">
Female person leg -- left
</div>
<div class="person__leg person__leg_side_right">
Female person leg -- right
</div>
<div class="person__hand person__hand_side_left">
Female person hand -- left
</div>
<div class="person__hand person__hand_side_right">
Female person hand -- right
</div>
</div>
</div>
从上面的代码中可以看到, 在这种情况下使用了默认的BEM编码方案, 因为我们只是使用BEM提供给我们的默认设置。你可以探索和使用更多命令和选项, 例如创建新页面, 新块或修改BEM HTML。我不会对此进行深入研究, 所有这些都可以在官方的BEM文档中找到。
优势与关注点
优点
- BEM非常适合维护。在某人完成一个大型项目后, 你不得不工作多少次, 而你又害怕更改任何事情而不会发生未知的崩溃?使用BEM时, 你知道该元素的确切用途以及它可能出现在哪个块中。
- 类名是合乎逻辑且直观的, 团队的每个成员都知道该元素在网站上的作用。 BEM为项目中的每个人提供了他们可以共享的声明性语法, 因此他们在同一页面上。
- BEM消除了嵌套CSS选择器。每个HTML元素都有其自己的CSS类, 你可以通过其名称知道其用途。一个选择器将它们全部统治。
关注点和常见错误
- 不要太深地嵌套。主要规则应该是使用不超过两个级别的父级和子级。
- 在开始你的块范围时要小心。这是一个常见的错误, 是当开发人员使用块时, 但他没有意识到在开发的后期, 同一块将具有主父块, 这可能会破坏嵌套规则。
- 避免SASS @extend。在此引用哈里·罗伯茨的话:
通过不在Sass中将类”绑定”在一起, 可以在视图中创建更多组合。 HTML具有更好的书面记录, 因为你可以看到每个类都在DOM上起作用。你的CSS变得更加苗条, 因为你不必在每次要创建新的UI时都创建新的占位符类(或将它们组合在一起的清单类)。
总结
当我第一次看到BEM编码方案时, 我的第一个想法是:
这些类太长了, 无法读写。
但是尝试一下之后, 现在我无法想象不使用它就开始一个新项目。对我来说, BEM极大地提高了我的代码可维护性, 我可以肯定地说, 每个将要”扔进”基于BEM的项目中的开发人员都将很快地赶上整个代码结构。
尽管如此, 社交网络上仍存在许多有关BEM的讨论。有人说BEM不好, 想知道为什么他们应该写这么长的名称类而不是只使用默认的HTML嵌套元素。嗯, 没有人说你必须喜欢BEM, 但事实是, 大多数前端开发人员都接受它, 并发现它非常有用。
相关:雇用自由职业者前端开发人员中的前3%。