Open FrankKai opened 6 years ago
BEM - Block Element Modfier(块元素编辑器)是一个很有用的方法,它可以帮助你创建出可以复用的前端组件和前端代码
它有如下3个特性:
下面将从3个方面来分析BEM到底是什么?
BEM是一个高可用的,强大的,而且简单的命名规范,它可以使得你的前端代码更加易读和理解,容易与他人协作,容易扩展,更加强壮和明确,关键是更加严谨。
BEM的方法,可以确保参与网站开发的每一个人,都能够使用一个代码库并且使用同一种语言。使用BEM格式的命名规范,可以从容应对需求变更。
BEM不仅仅是一种CSS命名的方法论,它其实对于浏览器渲染页面的性能有帮助。 BEM是如何通过减少选择器的复杂性,从而降低computed style计算的损耗的?
在小型手册网站上,如何组织你的风格通常不是一个大问题。你进入那里,写一些CSS,或者甚至是一些SASS。用SASS的生产设置将它编译成一个样式表,然后将它聚合到一起,将模块中的所有样式表都变成一个很好的整洁包。
但是,当涉及到更大,更复杂的项目时,组织代码的方式至少有以下三种方式是提高效率的关键:它影响编写代码需要多长时间,需要多少代码写和你的浏览器将不得不做多少加载。当你与团队合作并且高性能是必不可少的时候,这变得尤为重要。
这对于使用遗留代码的长期项目也是如此(请参阅“如何使用Sass和SMACSS来扩展和维护传统CSS” - 一些不错的SMACSS和BEM混合)。
目前有很多方法可以减少CSS占用空间,组织程序员之间的合作以及维护大型CSS代码库。这在Twitter,Facebook和Github等大型项目中很明显,但其他项目通常会很快成长为“巨大的CSS文件”状态。
我选择BEM而不采用其他方法的原因归结为:它比其他方法(即SMACSS)更容易混淆,但仍为我们提供了我们想要的良好体系结构(即OOCSS)以及可识别的术语。 Mark McDonnell,BEM可维护的CSS
听到BEM是方法论的关键元素 - Block,Element和Modifier的缩写,您一定不会感到惊讶。BEM严格的命名规则可以在这里找到。
我们现在来看一个使用BEM的元素,是Github的button:
我们可以使用一个普通按钮应对普通情况 ,再使用两种不同的应对不同情况。因为我们使用BEM的方式为Block 绑定了class,我们可以用任何想使用的标签去实现。(button,a 甚至是div)。但是命名规则告诉我们需要使用block-modifier-value的语法。 HTML:
<button class="button">
Normal button
</button>
<button class="button button--state-success">
Success button
</button>
<button class="button button--state-danger">
Danger button
</button>
CSS:
.button {
display: inline-block;
border-radius: 3px;
padding: 7px 12px;
border: 1px solid #D5D5D5;
background-image: linear-gradient(#EEE, #DDD);
font: 700 13px/18px Helvetica, arial;
}
.button--state-success {
color: #FFF;
background: #569E3D linear-gradient(#79D858, #569E3D) repeat-x;
border-color: #4A993E;
}
.button--state-danger {
color: #900;
}
-单元性:块的样式从来不依赖同页面其它的元素,所以你永远不会遇到级联问题。您还可以将完成的项目中的块转移到新项目中。
在计算机科学领域,只有2个非常难解决的问题:一个是缓存失效,而另一个则是命名。-Phil Karlton
有一个好的样式指导手册将会显著提高开发的速度,debug的速度,以及在原有代码上完成新功能的效率。然而不幸的是,大多数的CSS 代码在开发过程中毫无任何结构和命名的规范。从长远角度来看,这会导致最后产生的CSS代码库很难维护。
BEM方法确保每一个参加了同一网站开发项目的人,基于一套代码规范去开发,这样非常有利于团队成员理解彼此的代码,而且对于后续接手项目的同学来说,也是一件好事。
我们将对Block,Element,Modifier从naming,html,css三个方面进行分析。
封装一个只对自己有意义的实体。当blocks能够被嵌套而且彼此之间可以交互的时候,语义上他们是等价的;没有层级关系。没有DOM表示的整体实体。(例如控制器和模型也可以是块)
Block名字包含拉丁字母,数字和句号。当组合一个完整CSS class的时候,可以增加一个短的前缀:.block
任何DOM节点可以作为一个Block,只要它接受一个class名。<div class="block">...</div>
.block {color:#042;}
Block的一部分,当把它独立取出来时,没有任何实际意义。任何元素在语义上都是它自己的block紧密相连的。
Element的名字可能包含拉丁字母,数字,句号以及下划线。CSS class名由block名成加两个下划线再加element的名字,最后组织成一个块名。
任何的在Block中的DOM节点,都是一个element。在一个已知的block中,所有的element在语义上都是相等的。
<div class="block">
<span class="block__name"></span>
</div>
Good
.block__elem{color:#042};
Bad
.block .block__elem {color:#042;}
div.block__elem {color:#042;}
block或者element的flag。使用他们可以改变样式,行为或者是状态。
Modifier的名字可以包含拉丁字母,数字,句号以及下划线。CSS的class可以由block或者element名称后面加--组成,例如.block--mod或者.block__elem--mod,以及.block--color-black .block--color-red。复杂的modifier里由短线替代空格。
Modifier是一个额外的加在block或者element class名之后一个class 名。只为他们负责修改的blocks或者elements添加class,然后保持原有的class不变。 Good
<div class="block block--mod">...</div>
<div class="block block--size-big block--shadow-yes">...</div>
Bad
<div class="block--mod">...</div>
例子: 假设你的form Block由modifier:theme:"xmas" 以及simple:true 以及element:input 和submit,submit自己的modifier是disabled:true。 HTML:
<form class="form form--them-xmas form--simple"
<input class="form__input" type="text" />
<input class="form__submit form__submit--disabled" type="submit" />
</form>
CSS:
.form {}
.form--theme-xmas {}
.form--simple {}
.form__input {}
.form__submit {}
.form__submit--disabled {}
我们知道浏览器渲染页面分为js->style->layout(reflow)->paint->composite
这5步。
在style阶段是进行css规则的匹配,也就是通过选择器去选中元素。在这个过程中,如果使用过分复杂的选择器,其实会增大Recalculate style的时候消耗。
而通过为每一个元素设置一个自己命名的class的,可以使得浏览器直接通过class与元素做匹配,从而降低recalculate style的开销。
而BEM就是这样的一种方法论。
因为BEM为每个元素都增加了单独的class,无须通过CSS选择器一级一级一级一直查找到最深的层级。BEM使得匹配元素的时间复杂度从O(n)降低到O(1)。
假设有这样的伪代码:
function findElement(element) {
// complex css selectors cost O(n)
// BEM cost O(1)
}
注:如果对这一步骤有疑惑,可以查阅[译]Rendering Performance之Overview
用一句话概括就是:通过一个特殊命名的class去替代复杂的选择器,降低浏览器在 style 阶段的性能损耗。 具体原因可以在下文的分析中找到。
最简单的通过CSS去引用一个元素的方式只需要一个class:
.title {
/* styles */
}
但是,随着项目的成长,会出现更加复杂的CSS,例如我们可能用下面这种选择器:
.box:nth-last-child(-n+1) .title {
/* styles */
}
为了知道需要应用到浏览器的样式,必须这样问:“这是一个class名为title的元素,然后它的父元素需要找到第-n+1个class为box的元素?” 想把这个搞清楚的话,需要花费大量的时间,一般需要取决于使用的选择器和提问的浏览器。 上面这种消耗性能且可读性差的选择器可以改为下面这样:
.final-box-title {
/* styles */
}
我们需要花费一些时间去想class的名字,但是这对于浏览器来说会变得特别简单。 在之前的版本中,为了拿到最后一个元素,浏览器必须首先知道所有其他的元素所有信息,然后去判断后面的元素是不是最后一个子元素。这种方式显然比直接简单地通过class做匹配更加昂贵。
想知道更多Computed Style Calculation的信息,可以查看:[译]Rendering Performance之Reduce the Scope and Complexity of Style Calculations
在这篇【译】什么是CSS Modules ?我们为什么需要他们?的结尾处,明确指出CSS Modules不需要BEM,那么BEM到底是什么呢?
下面我将带领大家搞清楚前端领域的BEM到底是什么。 起初我以为BEM仅仅是一种项目内规范前端CSS命名的一种方法论,但后来才发现BEM原来其实还是一种性能优化的实践。 BEM降低了浏览器渲染过程
javascript->style->layout->paint->composite
中style阶段的选择器与元素之间的匹配消耗的时间。