scaactk

嵌套你的BEM?(Nesting Your BEM?)

scaactk · 2016-12-04翻译 · 988阅读 原文链接

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

我是一个BEM的拥护和支持者。并且已经持续了很多年。这是有点有趣的期待。当然, 它给我提供了很多的东西:

  • 软封装 这有助于减少命名冲突。
  • 自定义的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中的嵌套

在很长的一段时间内,我曾积极主张CSS中的嵌套是一件坏事情,因为:

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

总之, 保持你的CSS选择器的简短性.

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

特异性

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

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

当我们在讨论如何处理好一致性的问题时:我们指的是它的特异性图尽可能的平缓。

如果我们观察下面这一系列的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之间有着完全相同的特异性。这是一个很漂亮的平缓特异性图:

Graph showing low and flat specificity
查看大图

一旦我们像下面这样去嵌套这些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 { }

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

Graph showing changes in specificity
查看大图

哦,天啊! 峰值(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类依旧非常轻便。

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

Graph showing higher but still flat specificity
查看大图

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

用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 */ }

}

看一个关于Sassmeister的小例子

实际结果

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

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

缺陷

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

用例

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

.grid.grid {  }

  .grid .grid__item {  }

用还是不用?

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

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

译者scaactk尚未开通打赏功能

相关文章