BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?

时间:2021-05-13 19:47:10

https://medium.com/@andersonorui_/bem-sass-and-bootstrap-9f89dc07d20f

Bootstrap是一个“HTML,CSS和Javascript的框架,用于开发responsive,mobile first project";

SASS是一个css扩展预编译工具;

BEM是一个解决css可维护可扩展的方法原则

我通常使用LESS,一个原因是Bootstrap本身是Less写的。注意SASS和LESS有一些区别可能会让你感觉很奇怪:

变量覆盖的原则不同: LESS:后面定义的会覆盖前面的,并且在整个代码中都以后面定义的值为准;SASS则是先定义的会先生效直到遇到重新覆盖定义为止。

注意这个区别对你使用LESS/SASS来开发bootstrap的定制设计时,比如variable.less/sass文件(也就是所谓bootstrap.theme.less),其位置就很重要了,对于less,则需要你的客制化variable放到后面引入,而对于sass则需要最早引入。

Cleaning up css classes

现在已经很少使用photoshop或者illustrator了,几乎所有工作从项目开始时就直接在html/css/js中进行,当然有时可能我会使用sketch3来做一下brainstrom或者创建ui elements。也正因为此,我的代码越来越乱,以至于不得不重构代码。当然这个workflow也是我所喜欢的,因为在项目启动时我们几乎不知道我们打算如何去解决问题,我们无法看到所有的patterns,深思熟虑有些浪费时间,所以我往往喜欢在当项目有一个始终一致的模样时才来做这个工作。这样可能更有效果。

重构之前我可能有下面的html markup,

<div id=”social-newsletter”>
<div class=”container”>
<header class=”text-center”>
<h1 class=”bottom top”>Acompanhe as novidades</h1>
</header>
<div class=”row”>
<div class=”social col-xs-6 col-sm-2 col-md-2">
<div class=”facebook block”>
<div class=”centered”>
<a href=”http://www.facebook.com" title=”Facebook”>
<span class=”sr-only”>facebook</span>
<span class=”fa fa-facebook fa-4x”></span>
</a>
</div>
</div>
</div>
<div class=”social col-xs-6 col-sm-2 col-md-2">
<div class=”twitter block”>
<div class=”centered”>
<a href=”http://www.twitter.com" title=”Twitter”>
<span class=”sr-only”>twitter</span>
<span class=”fa fa-twitter fa-4x”></span>
</a>
</div>
</div>
</div>
<div class=”social col-xs-6 col-sm-2 col-md-2">
<div class=”youtube block”>
<div class=”centered”>
<a href=”http://www.youtube.com" title=”YouTube”>
<span class=”sr-only”>youtube</span>
<span class=”fa fa-youtube fa-4x”></span>
</a>
</div>
</div>
</div>
<div class=”social col-xs-6 col-sm-2 col-md-2">
<div class=”instagram block”>
<div class=”centered”>
<a href=”http://www.instagram.com" title=”Instagram”>
<span class=”sr-only”>instagram</span>
<span class=”fa fa-instagram fa-4x”></span>
</a>
</div>
</div>
</div>
<div class=”newsletter col-xs-12 col-sm-4 col-md-4">
<form>
<div class=”block”>
<label class=”centered”>Assine nossa newsletter</label>
</div>
<input type=”email” placeholder=”Insira seu email” class=”col-xs-12 col-sm-12 col-md-12"></input>
<button type=”submit” class=”btn btn-danger col-xs-12 col-sm-12 col-md-12">Cadastrar email <i class=”fa fa-paper-plane fa-2x pull-right”></i></button>
</form>
</div>
</div>
</div>
</div>

通过以BEM方法论,SASS工具支持,可能优化为下面的样子:

<div class=”social-links js-social-links”>
<header class=”social-links—header”>
<h1>Acompanhe as novidades</h1>
</header>
<div class=”social-links—content”>
<div class=”social-links—link js-social-link”>
<a class=”link—facebook” href=”http://www.facebook.com" title=”Facebook”>
<span class=”sr-only”>facebook</span>
<span class=”fa fa-facebook”></span>
</a>
</div>
<div class=”social-links—link js-social-link”>
<a class=”link—twitter” href=”http://www.twitter.com" title=”Twitter”>
<span class=”sr-only”>twitter</span>
<span class=”fa fa-twitter”></span>
</a>
</div>
<div class=”social-links—link js-social-link”>
<a class=”link—youtube” href=”http://youtube.com" title=”YouTube”>
<span class=”sr-only”>youtube</span>
<span class=”fa fa-youtube”></span>
</a>
</div>
<div class=”social-links—link js-social-link”>
<a class=”link—instagram” href=”http://www.instagram.com" title=”Instagram”>
<span class=”sr-only”>instagram</span>
<span class=”fa fa-instagram”></span>
</a>
</div>
<form class=”social-links—newsletter js-newsletter”>
<label class=”newsletter—label”><span>Assine nossa newsletter</span></label>
<input class=”newsletter—input” type=”email” placeholder=”Insira seu email”/>
<button class=”newsletter—submit” type=”submit”><span>Cadastrar email</span></button>
</form>
</div>
</div>

得到的UI效果是这个样子的:

BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?

为了达成上面的亩i奥--更加可读,可理解和富含语义,我需要理解SASS是如何工作的,以及BEM后面所隐含的概念。

BEM: Block. Eelement . Modifier

BEM是block,element,modifier的首字母缩写。它的核心想法是通过遵循一套规则使得everything modular---这样将易于重用易于维护,也更加容易理解和自描述。从BEM网站上,我们摘抄以下:

“A block is A logically and functionally independent page component, the equivalent of a component in Web Components. A block encapsulates behavior (JavaScript), templates, styles (CSS), and other implementation technologies. Blocks being independent allows for their re-use, as well as facilitating the project development and support process.Blocks can be implemented in one or more technologies, for example:
  • behavior — JavaScript, CoffeeScript
  • appearance — CSS, Stylus, Sass
  • templates — BEMHTML, BH, Jade, Handlebars, XSL
  • documentation — Markdown, Wiki, XML.
"
A block can be either simple or compound(containing other blocks),比如下面的例子是一个search form block:
BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?
“An element is a part of a block that performs a certain function. Elements are context-dependent: they only make sense in the context of the block they belong to.”
例如:一个input field和一个button是构成search block的elements:
BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?
“A modifier is a property of a block or an element that alters its look or behavior.”

Means of Describing Pages and Templates

blocks和elements一起构成了page content。除了简单地布放在页面上,他们的安排也非常重要。Blocks(or elements)可能按照一定的顺序一个挨着一个的排列。例如,在一个电商网站上,商品一个个罗列:

BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?

或者比如menu items:

BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?

Blocks也可能被包含在其他的block中,比如,一个Head Block包含了logo,searchbox,authblock,menu block。BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?

而且,我们的building blocks需要一种使用plain text的方式来描述页面的布局。为了实现这一点,每一个block和element都需要很好的命名。Block names应该在整个项目范围内是唯一的;只有相同的block的不同实例化需要使用完全相同的block类,Element名称必须在所属block范围内唯一,一个element可以被在block范围内被重复使用任意次数。

希望了解更多,可以直接访问BEM的网站: http://bem.info/method/definitions/

总结以下,BEM的想法就是要创建一个下面的元素组织架构:

- block

- block__element

- block__element__modifier

<div class="menu menu_hidden">  <span class="menu__item"></span> </div> <div class="menu menu_theme_morning-forest"> <span class="menu__item"></span></div>

BEM TREE

{
block: 'page',
content: {
block: 'head',
content: [
{ block: 'menu', content: ... },
{
elem: 'column',
content: { block: 'logo' }
},
{
elem: 'column',
content: [
{
block: 'search',
content: [
{ elem: 'input' },
{
elem: 'button',
content: 'Search'
}
]
}
]
},
{
elem: 'column',
content: {
block: 'auth',
content: ...
}
}
]
}
}

element和block可以互相包含。。。

一般来说,随着项目的发展,blocks倾向于被添加,被删除或者在页面上被移动。比如,你可能希望调换logo和auth block的位置,或者希望将menu放到search block的下方,为了让这个变更过程更加方便简单,要求blocks必须是independent互相独立的

所谓independent block是以允许放置在页面中的任何地方的方式来实现的,包括随意地嵌入到其他的block中。

Independent CSS

从css角度来看这意味着:一个block(or an element)必须有一个唯一的"name"(a css class);HTML elements必须不能在CSS selectors(.menu td)中使用,因为这些包含html tag的selectors固有地不具有context-free的特性;Cascading selectors for several blocks should be avoided:不要使用层叠特性!

Naming for independent CSS classes

一种可能的命名方案是:

  • 一个block的css class name和他的block name是一致的
  • 一个element的css class name由block name+element name组成
<ul class="menu">
<li class="menu__item">
...
</li>
<li class="menu__item">
...
</li>
</ul>

很有必要将block name包含在一个element的css class name中,因为这将最小化层叠的可能。同时注意使用一致的seperator(这里使用的是__),这对于允许自动化的工具介入开发流程很有帮助。

当然你可以使用其他的命名方式,我们推荐的方式是:

  • Block name: block name is a keyword that makes sence what is a block about. A block name may be composed of serveral words seperated with hyphen(我这里推荐是blockname由一个或多个单词直接连接而成): bbbb
  • Block prefix: 一个block name通常有一个prefix来帮助指示block的purpose
    •   b-  :比如b-menu-horiz  有实实在在的apperance;   b-bbbb
    • i-   :比如i-menu  这是一个抽象的block它自己并没有外观,主要用于实现某种功能而存在,比如它提供了一个功能,被b-menu-horiz block加以使用 i-bbbb
    • l-   : l-bbbbb  表示一个layout的block
  • Element name: 全名称的element name用于指示这个元素属于那一个block,比如 b-blockname__elementname,b-menu-horiz__item, b-popup__content bbbb_eeee
  • Block modifier: 全名称的modifier block用于指示它属于哪个block, 比如b-block-name_modifier-name_modifier-value, b-link_type_pseudo,b-menu-horiz_type_simple,b-popup_direction_up   bbbb__mmmm
  • Element modifier's name: 全名称用于指示他属于哪一个元素(并且哪一个block),b-block-name__element-name_modifier-name_modifer-value,比如b-menu-horiz__item_state_current  bbbb_eeee__mmmm-vvvv

Independent templates

从模版引擎的角度来看,block independence意味着:

  • blocks and elements必须在input data中描述,blocks(or elements)must have unique "names" to make things like "Menu" should be placed here"expressible in our templates;
  • Blocks may appear anywhere in a BEM tree

Modifiers For Elements And Blocks

我们如果需要创建一个和已经存在的一个block非常接近的block,但是可能外观稍微有些区别,比如,我们有这样一个任务:

在footer区域增加一个menu block,使用另外一种layout

BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?

为了避免再开发另外一个block,我们可以使用一个Modifier.

一个Modifier是一个block或者element的属性,该属性仅仅改变block/element的外观或者行为。一个modifier有一个name和value,多个modifier可以同时使用。

比如,一个block modifer specifies background color,再比如一个元素的modifier更改look of the "current"item.

从input data角度来看,在一个BEM tree中,modifier是一个描述block/element的实体属性

<b:menu m:size="big" m:type="buttons">
...
</b:menu>

同样地,可以是有那个json来描述

{
block: 'menu',
mods: [
{ size: 'big' },
{ type: 'buttons' }
]
}

从css角度来看,一个modifier是一个额外的css 类用于修饰block或者element

<ul class="menu menu__size-big menu__type-buttons">
...
</ul>
.menu_size_big {
// CSS code to specify height
}
.menu_type_buttons .menu__item {
// CSS code to change item's look
}

同样地,对于element modifier可以以类似的方式来实现,比如current menu item可以这样来实现:

<b:menu>
<e:item>Index<e:item>
<e:item m:state="current">Products</e:item>
<e:item>Contact<e:item>
</b:menu>
{
block: 'menu',
content: [
{ elem: 'item', content: 'Index' },
{
elem: 'item',
mods: { 'state' : 'current' },
content: 'Products'
},
{ elem: 'item', content: 'Contact' }
]
}
<div class="menu">
<ul class="menu__layout">
<li class="menu__layout-unit">
<div class="menu__item">Index</div>
</li>
<li class="menu__layout-unit">
<div class="menu__item menu__item_state_current">Products</div>
</li>
<li class="menu__layout-unit">
<div class="menu__item">Contact</div>
</li>
</ul>
</div>
.menu__item_state_current {
font-weight: bold;
}

Blocks Consistency

一个website有一个Button block,该block可能包含特定的动态行为,比如当一个block被hover时,要求更改它的appearance.

manager可能会问:在另外一个page中使用同样的button.

虽然对于一个block有了css implementation,但是这是不够的。重用一个block也意味着重用它的行为,而该行为使用javascript来描述。

所以一个block必须知道关于它自己的所有事情。为了实现一个block,我们使用各种技术来描述他的外观和行为---我们称之为multilingualism.

Multilingualism presentation是一个对block从各个programming languages的角度来描述的方法,该方法能够准确描述清楚该block的view和functionality。

To have a block present on a page as a UI element, we need to implement it in the following techs:

  • Templates(XSL,TT2,Javascript,etc), which turn block declarations into HTML code;
  • CSS that describe apperance of the block;

如果一个block有动态的behaviour,我们还需要添加

  • 一个javascript implmentation for the block,

everything that constitues a block is a technology, including images.

http://www.smashingmagazine.com/a-new-front-end-methodology-bem-blocks-reiteration/

我们先来理解我们想达到的目标并且理解layout的结构:

BEM,SASS,LESS,bootstrap:如何有效地将这些方法,工具和框架聪明地整合?

然后我们需要定义属于不同context的我们的类了:

1.首先定义整个context block .social-links

2.在.social-links里面,创建两个其他的blocks: .social-links--header和.social-links--content

3.在.social-links--header中只有一个h1,唯一的元素;

4。在.social-links--content block中,有四个elements(.social-links--link)和一个block(.social-links--newsletter)

5.在.social-links--newsletter block中,我有3个elements: .newsletter--label,.newsletter--input,.newsletter--button

或许,严格按照BEM命名规范,我需要使用.social-links--newsletter--label,.social-links--newsletter--input,但是我知道我不会在其他地方使用这个newsletter block,所以就简化为一个短小的class了(实际上这个假设本身应该是有问题的!)

BEM方法论使得创建一个class是很简单的,你只需要知道context(block level),然后按照规则来套用就可以了。

Bootstrap

Bootstrap有很多漂亮的功能,但是我只想演示以下如何在SASS中使用guid class.

在bootstrap中,我们有下面一些@mixins来创建一个grid系统:

container-fixed()
make-row()
make-xs-column()
make-xs-column-offset()
make-xs-column-push()
make-xs-column-pull()
make-sm-column-offset()
make-sm-column-push()
make-sm-column-pull()
make-md-column-offset()
make-md-column-push()
make-md-column-pull()
make-lg-column-offset()
make-lg-column-push()
make-lg-column-pull()
clearfix()

这样我们可以像下面的代码一样来做设计:

section {
@include make-row();
article {
@include make-xs-column(12);
@include make-sm-column(7);
@include make-md-column(8);
}
aside {
@include make-xs-column(12);
@include make-sm-column(3);
@include make-md-column(4);
}
}

在不使用BEM方法论之前,我们可能这样书写HTML markup:

<section class=”row”>
<article class=”col-xs-12 col-sm-7 col-md-8">
Article content
</article>
<aside class=”col-xs-12 col-sm-3 col-md-4">
Aside content
</aside>
</section>

而当我们引入BEM以及借用BOOTSTRAP LESS/SASS的代码重构后,这样书写html markup:

<section>
<article>
Article content
</article>
<aside>
Aside content
</aside>
</section>

这样的HTML更加清晰和易于理解。

SASS

我们再来看看下面两片代码:

<div class=”social-links—content”>
<div class=”social col-xs-6 col-sm-2 col-md-2">
<div class=”twitter block”>
<div class=”centered”>
<a href=”http://www.twitter.com" title=”Twitter”>
<span class=”sr-only”>twitter</span>
<span class=”fa fa-twitter fa-4x”></span>
</a>
</div>
</div>
</div>
</div>

以及去除bootstrap的预定义class后的代码:

<div class=”social-links—content”>
<div class=”social-links—link js-social-link”>
<a class=”link—twitter” href=”http://www.twitter.com" title=”Twitter”>
<span class=”sr-only”>twitter</span>
<span class=”fa fa-twitter”></span>
</a>
</div>
</div>

既然我们的想法是清理html代码,使得其更加易读,我删除了所有的bootstrap grid class,取而代之的是通过@extend,@include直接把grid class插入到css中去:

.social-links—content {
@extend .container; .social-link {
@include make-xs-column(6);
@include make-sm-column(2);
}
}

然后,我创建一个@mixin来在垂直方向向中间对齐icons:

@mixin vertical-align() {
display: block;
&:before {
content: ‘’;
display: inline-block;
height: 100%;
vertical-align: middle;
margin-right: -0.25em;
}
> * {
display: inline-block;
vertical-align: middle;
width: 98%;
}
}

这样我就可以这样更新.social-links--content:

.social-links—content {
@extend .container; .social-link {
@include make-xs-column(6);
@include make-sm-column(2);
a {
@include vertical-align();
}
}
}

从而将HTML中的div.block和div.centered删除,也就是从下面的HTML:

<div class=”social col-xs-6 col-sm-2 col-md-2">
<div class=”twitter block”>
<div class=”centered”>
<a href=”http://www.twitter.com" title=”Twitter”>
<span class=”sr-only”>twitter</span>
<span class=”fa fa-twitter fa-4x”></span>
</a>
</div>
</div>
</div>

变成了下面的BEM风格的代码:

<div class=”social-links—link js-social-link”>
<a class=”link—twitter” href=”http://www.twitter.com" title=”Twitter”>
<span class=”sr-only”>twitter</span>
<span class=”fa fa-twitter”></span>
</a>
</div>

使用LESS 嵌套feature实现BEM命名方法:

在css开发中,我们非常喜欢使用nested方式来组织css元素,因为这种方式我们可以清晰地看到元素间的层次关系(尽管严格使用BEM命名方式也可以看出来),但是如果一般性地嵌套,则输出的css为后代选择器,一种可行的方法是使用&特殊字符(SASS中使用

@at-root #{&}__element
.btn{
width: 100px;
&__icon{ //可以看到icon作为btn block的一个元素
color: blue;
&--big{ //可以看到.btn__icon--big作为btn block下面的icon元素的一个modifier
font-size: 20px;
}
}
&--primary{ //可以看到.btn--primary作为btn block的modifier
background-color: blue;
}
}
上面的LESS代码将按照BEM模式的组织方式生成出来的CSS代码如下:
.btn{width:100px}
.btn__icon{color:#00f}
.btn__icon--big{font-size:20px}
.btn--primary{background-color:#00f}

http://www.smashingmagazine.com/2014/07/bem-methodology-for-small-projects/

http://www.smashingmagazine.com/2013/02/the-history-of-the-bem-methodology/