原文链接:http://csswizardry.com/2016/11/nesting-your-bem/

在我开始这篇文章之前,我得说这不是一个建议或者是新的“练习指南”。这只是我自己的一些思想狂潮。

我是一个[<cite>BEM</cite>](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/)的拥护和支持者。并且已经持续了很多年。这是有点有趣的期待。当然, 它给我提供了很多的东西:

* **软封装** 这有助于减少命名冲突。
* **自定义的CSS** 这帮助我理解DOM节点彼此之间的关系。
* **目标的选择** 这有助于减少子树之间的冲突,并且避免捕获太多的节点。
* **管理方式的特殊性** 这是一个很大的亮点。
* **严格的实现规则** 这能够防止我使用给定上下文以外的classes。

除了最后一点只有一半是真的......

BEM告诉我们,一个class, 例如: `.widget__title`, 仅仅只能被用在`.widget`内。 但是这仅仅是通过了协议的规定。开发人员可能会把`.widget_title`放在`.model`之内,并且仍能正常工作。这是因为:

* 他们之前没有见过BEM,或者是不知道执行它的方法
* 他们很懒惰,并且发现———即使是不应该,但是他们可以重用`.modal`内的`.widget_title`样式的,然后这样能够提前5分钟完成工作

他们能够做到这一点,它也能为他们工作:东西仍旧能够正确显示。不会因此导致额外的错误,因为 BEM 仅仅是一个规定,并且规定需要一致的认同。

为了规避这一点,我们可以像下面这样地来写CSS:

```
.widget { }

.widget .widget__title { }
```

现在开发者不能在`.modal`内使用`.widget_title`,因为我们告诉了我们的CSS:`wideget_title`仅仅在我们把它放到`.widget`中才能起效。现在我们开始执行这些事情,它将防止滥用。

这里还有一个问题:嵌套

## CSS中的嵌套

在很长的一段时间内,我曾[<cite>积极主张</cite>](http://cssguidelin.es/#specificity)CSS中的嵌套是一件坏事情,因为:

* 增加了特性 (这些应该始终要管理好);
* 引入了对存储位置的依懒 (不灵活系统的一个标志);
* 减少了可移植性 (这意味着我们不能随意移动它);
* 增加了脆弱性 (嵌套意味着选择器出错的可能性增加了)。

总之, [<cite>保持你的CSS选择器的简短性</cite>](http://csswizardry.com/2012/05/keep-your-css-selectors-short/).

但是在使用了嵌套BEM的情况中,我们看到了嵌套给我们带来了实实在在的好处。但是我们要如何处理这些缺陷呢?

## 特异性

注意,通常情况下始终保持低特异性是很重要的。这完全正确,并且这是一个很好的建议。但是,这里和我们熟知的那些是有一点区别的。人们在说特异性应当在任何情况下都处理好的时候,真正的意思是,我们应当保持一致性,并且选择器之间的区别很小。

理论上来说(但是,亲爱的,请不要尝试这样做),一个项目的唯一选择器是ID选择器,这将很好地管理特异性:特异性普遍是高的,但至少所有都符合并且相等。

当我们在讨论如何处理好一致性的问题时:我们指的是它的[<meta>特异性图<meta>](http://csswizardry.com/2014/10/the-specificity-graph/)尽可能的平缓。

如果我们观察下面这一系列的CSS组件:

```
.nav-primary { }

.nav-primary__item { }

.nav-primary__link { }

.masthead { }

.masthead__media { }

.masthead__text { }

.masthead__title { }

.sub-content { }

.sub-content__title { }

.sub-content__title--featured { }

.sub-content__img { }
```

…我们发现,他们每个class之间有着完全相同的特异性。这是一个很漂亮的平缓特异性图:

<figure>![Graph showing low and flat specificity](http://p0.qhimg.com/t01298e8f9265d223bb.png)

<figcaption>[<cute>查看大图</cute>](http://csswizardry.com/wp-content/uploads/2016/11/graph-specificity-01.png)</figcaption>

</figure>

一旦我们像下面这样去嵌套这些classes:

```
.nav-primary { }

.nav-primary .nav-primary__item { }

.nav-primary .nav-primary__link { }

.masthead { }

.masthead .masthead__media { }

.masthead .masthead__text { }

.masthead .masthead__title { }

.sub-content { }

.sub-content .sub-content__title { }

.sub-content .sub-content__title--featured { }

.sub-content .sub-content__img { }
```

…我们看到的特异性图将会是这样的:

<figure>![Graph showing changes in specificity](http://p0.qhimg.com/t01afba20a956d400fc.png)

<figcaption>[<meta>查看大图</meta>](http://csswizardry.com/wp-content/uploads/2016/11/graph-specificity-02.png)</figcaption>

</figure>

哦,天啊! 峰值(Spikes)! 峰值正是我们想要避免的,因为他们代表着项目中非常接近的选择器之间的特异性的波动。

这里我们看了嵌套中特异性导致的一些缺陷(Here we are visualising the specificity downside to nesting.)我们能避开它吗?怎样做?

## 链接第一个class

如果我们要链接第一个class (the Block) 到它自身, 像这样:

```
.nav-primary.nav-primary { }

.nav-primary .nav-primary__item { }

.nav-primary .nav-primary__link { }

.masthead.masthead { }

.masthead .masthead__media { }

.masthead .masthead__text { }

.masthead .masthead__title { }

.sub-content.sub-content { }

.sub-content .sub-content__title { }

.sub-content .sub-content__title--featured { }

.sub-content .sub-content__img { }
```

…我们可以使其特异性地匹配所有无副作用的嵌套元素:

* 我们不需要知道这个Block在DOM中的位置,因此我们不会依据一些可能更改的位置而去增加它的特异性
* 我们并没有连接到另一个不同的或者是特定的元素或者类。这意味着Block类依旧非常轻便。

这种特异性的增加完全依据它自身,现在我们看到了这样的特异性图:

<figure>![Graph showing higher but still flat specificity](http://p0.qhimg.com/t01cecef3a98ac52bbf.png)

<figcaption>[<cute>查看大图</cute>](http://csswizardry.com/wp-content/uploads/2016/11/graph-specificity-03.png)</figcaption>

</figure>

比第一张图高,但是仍旧非常平缓。尽管我们我们的特异性是两级的高度,它仍旧被很好的掌控:我们选择器组件没有特殊的权重。

## 用Sass简化

为了使嵌套以及链接更加简化,我们可以使用预处理,在这里是Sass:

我们应当都熟悉如何在Sass中嵌套常规选择器:

```
.nav-primary {

.nav-primary__item { }

.nav-primary__link { }

}
```

这给我们带来了,正如我们所期待的:

```
.nav-primary { }

.nav-primary .nav-primary__item { }

.nav-primary .nav-primary__link { }
```

但是我们如何快速并且有效地将第一个class链接到他自身?像这样:

```
.nav-primary {

&#{&} { }

.nav-primary__item { }

.nav-primary__link { }

}
```

通过使用 `&#{&}`, 我们可以将当前的class链接到它自身。这意味着我们所有Block的样式(在这种情况下, `.nav-primary`) 都在这里:

```
.nav-primary {

&#{&} { /* Block styles */ }

}
```

[<cute>看一个关于Sassmeister的小例子</cute>](http://www.sassmeister.com/gist/a14e5b242ee6b20932dd44df0a3d215c)

## 实际结果

现在,我们的处境是事实上在强制地使用,并且主动地阻止选择器的起效——如果我们主动地将它们从DOM中正确的部分移出的话。这可以帮助我们在其他开发者并不知道BEM如何起效的环境下工作,或者是那些倾向于使劲折腾直到一切看上去正常的人。

我们也有一个管理所有classes(虽然是增加的)的特异性

### 缺陷

我们正在增加一些特异性,这通常正是我们应该永远努力去避免的。

## 用例

如果你想要尝试延伸这种技术,开始之前,这里有必要确认一些关键的用例。第一个展现在我脑中的是 网格系统(grid systems) 一次又一次地,我看见开发者们在`.grid`父类外尝试去使用`.grid__item`类。所以,如果我打算开始使用这种技术的话,我会从这里开始:

```
.grid.grid { }

.grid .grid__item { }
```

## 用还是不用?

我不是很确定,正如我在开头说的那样,这不是一项我非常推荐并且致力于推行的技术。我仅仅是想要提出它,给大家作为一个参考,尤其是对那些发现自己正处于一个,其他开发者都在轻易地滥用CSS的环境中的开发者。

但是,我想说的是:如果你已经嵌套了你的BEM,请返回,通过链接你的第一个class去拉平你的特异性图。

译文:Nesting Your BEM?的更多相关文章

  1. 和BEM的战斗:10个常见问题及如何避免

    原文链接: https://segmentfault.com/a/1190000006135647 无论你是刚刚发现BEM或者已经是个中熟手(作为web术语来说),你可能已经意识到它是一种有用的方法. ...

  2. CSS命名规范——BEM思想(非常赞的规范)

    人们问我最多的问题之一是在CSS类名中“--”和“__”是什么意思?它们的出现是源于BEM和Nicolas Gallagher... BEM的意思就是块(block).元素(element).修饰符( ...

  3. Google C++单元测试框架GoogleTest---AdvancedGuide(译文)下

    因为AdvancedGuide文档太长,分上下两部分,本文档接googletest--AdvancedGuide(译文)上:Google C++单元测试框架GoogleTest---AdvancedG ...

  4. Atitit.css 规范 bem  项目中 CSS 的组织和管理

    Atitit.css 规范 bem  项目中 CSS 的组织和管理 1. 什么是BEM?1 1.1. 块(Block)2 1.2. 元素(Element)2 1.3. BEM树(和DOM树类似).3 ...

  5. Google C++单元测试框架GoogleTest---AdvancedGuide(译文)上

    本文是gtest高级测试指南的译文,由于文章太长,分上下两部分. 一.简介 本文档将向您展示更多的断言,以及如何构造复杂的失败消息,传播致命的故障,重用和加速您的测试夹具,并在您的测试使用各种标志. ...

  6. CSS 命名管理 之 BEM

    好吧,将 BEM 简单的解释为 “Block-Element-Modifier“, 其实是个不负责任的做法.鬼知道 Block 是什么啊?所以,看了一些似懂非懂的中文解释之后,自己还是得去找些英文来读 ...

  7. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:实现基本的CRUD功能

    英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列 ...

  8. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:排序、筛选和分页

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第三篇:排序.筛选和分页 原文:Sort ...

  9. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的连接恢复和命令拦截

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第四篇:MVC程序中实体框架的连接恢复和 ...

随机推荐

  1. ASP.NET AJAX Control Toolkit

    https://ajaxcontroltoolkit.codeplex.com/ 警告 7 未能找到引用的组件“Antlr3.Runtime”. 警告 6 未能找到引用的组件“HtmlAgilityP ...

  2. fibonacci封闭公式及矩阵连乘

    Description The Fibonacci sequence is the sequence of numbers such that every element is equal to th ...

  3. linux下mysql忘记root密码解决方法

    如果使用 MySQL 数据库忘记了root账号密码,可以通过调节配置文件,跳过密码的方式登数据库, 在数据库里面修改账号密码,一般默认的账号是 root 1.编辑 MySQL 配置文件 my.cnf ...

  4. 打印机问题win7 和xp

    服务器端问题,重启如下服务 net stop "print spooler" net start "print spooler" gpedit.msc 本地计算 ...

  5. iOS 七牛云上传并获取图片----【客户端】

           最近做了七牛云存储的有关内容,涉及到与后台交互获取验证的token,无奈,后台自命清高,不与理会,没办法呀,于是自己搞呗.首先呢在在七牛上注册一个账号,然后呢添加一个存储空间这时候空间名 ...

  6. Automapper扩展方法

    问题描述 系统中实现了一个自定义的PagedList /// <summary> /// Paged list interface /// </summary> public ...

  7. 一台电脑存放多个git账户的多个rsa秘钥

    未命名.html div.oembedall-githubrepos{border:1px solid #DDD;border-radius:4px;list-style-type:none;marg ...

  8. HillStone上网认证客户端

    公司上网认证服务器从原来网康变更成山石(HillStone),原来网康是有认证客户端的,运行在系统托盘区,现在的Hillstone是通过网页页面认证的,要上网,这个认证页面就需要一直打开在那里.碰到异 ...

  9. UWP开发中的方向传感器

    在UWP开发中,我们能使用的到方向有三种: OrientationSensor下的四元数:Compass罗盘的HeadingMagneticNorth:以及SimpleOrientationSenso ...

  10. iOS设计模式之中介者模式

    中介者模式 基本理解 中介者模式又叫做调停者模式,其实就是中间人或者调停者的意思. 尽管将一个系统分割成许多对象通常可以增加可复用性,但是对象之间的连接又降低了可复用性. 如果两个类不必彼此直接通信, ...