CSS设计规则BEM快速入门手册

关于BEM

BEM是Block Element Modifier缩写, BEM通过以下3个维度来进行CSS设计及命名的.

  • Block 代码里大括号
  • Element 大括号里的元素
  • Modifier 标记上述2个的状态

目标

  • BEM中特别以MindBEMding命名规则为基础
  • 使用SCSS
  • 通过基本的简单类来设计
  • SCSS与HTML完美分离

命名规则

类block__element–modifier形式,block与element之间通过2个’_’连接,element与modifier之间通过2个’-‘连接.
block为多个单词时,单词与单词之间通过1个’-‘连接. element, modifier同样处理.

比如:
block名为artile-list中有个叫artile-title的element时, 这个element的类名为article-list__article-title

文件规则

以1个block为单位生成对应SCSS文件. 反之如果1个SCSS文件里定义多个block的话, 则说明违反的设计原则. 文件名为 block名字.scss.

例如上述的block article-list, 它所对应的文件名为article-list.scss.

在CSS中所有的名称都是全局有效, 因为命名重复导致可维护性下降. 在BEM中通过严格的命名规则来解决这个问题. 不管element名称重复多少个都没有影响.

但是, 得避免block名即文件名重复. 遵守以1个文件对应1个block的原则, 就可以很好的避免block名称重复的问题了.

SCSS

规则

  • 使用嵌套和父节点名称来定义element
    • element内部不允许嵌套定义其它element
  • 使用嵌套和父节点名称来定义modifier
    • modifier使用placeholder selector,来描述差异的部分
使用嵌套和父节点名称来定义element

比如article-list__article-title可定义如下

.article-list {
  width: 100%;

  &__article-title {
    font-size: 20px;
  }
}
element内部不允许嵌套定义其它element

假设有HTML如下

<div class="article-list">
  <div class="article-list__article-title">
    タイトル
    <span class="article-list__article-subtitle">サブタイトル</span>
  </div>
</div>

下面是违反BEM命名规则的范例

.article-list {
  width: 100%;

  &__article-title {
    font-size: 20px;

    &__article-subtitle {
      font-size: 16px;
    }
  }
}

在HTML中,article-subtitlearticle-title的子节点,但如果在SCSS中也article-title中嵌套的话,即导致SCSS不能很好的将SCSS和HTML内容分离出来。

为了使HTML和SCSS分离, 不管HTML构造如何变化, element应避免关联除block以外的父节点。 SCSS写法如下

.article-list {
  width: 100%;

  &__article-title {
    font-size: 20px;
  }

  &__article-subtitle {
    font-size: 16px;
  }
}
使用嵌套和父节点名称来定义modifier

假设想控制article-title的文字颜色为是否为红色时, 则需要嵌套定义article-titlemodifier

.article-list {
  width: 100%;

  &__article-title {
    font-size: 20px;

    &--red {
      // 后续章节定义,此处无任何功能
    }
  }

  &__article-subtitle {
    font-size: 16px;
  }
}

对应的HTML内容如下

<div class="article-list">
  <div class="article-list__article-title">
    非红色标题
    <span class="article-list__article-subtitle">子标题</span>
  </div>

  <div class="article-list__article-title--red">
    红色标题
    <span class="article-list__article-subtitle">子标题</span>
  </div>
</div>
modifier使用placeholder selector,来描述差异的部分

article-list__article-title--red样式只是颜色为红色时, 其他样式全部和article-list__article-title 保持一致时,定义如下

.article-list {
  width: 100%;

  &__article-title {
    font-size: 20px;

    &--red {
      font-size: 20px;
      color: red;
    }
  }

  &__article-subtitle {
    font-size: 16px;
  }
}

这里font-size: 20px; 是公共的组成部分, 这样子定义带的缺点就是导致公共组成内容重复增加。 当公共部分变化时, 保持一致所带来的代码维护就变得相当麻烦了。 当modifier里增加gren, blue等其他颜色时, 就会更难维护。那么如何避免呢?

为了避免上述状况,这里就需要借助placeholder selector来管理公共部分, modifier针对每个element做差异处理。

.article-list {
  width: 100%;

  %__article-title {
    font-size: 20px;
  }

  &__article-title {
    @extend %__article-title;

    &--red {
      @extend %__article-title;
      color: red;
    }
  }

  &__article-subtitle {
    font-size: 16px;
  }
}

当增加gren, blue等其他颜色时, 代码依然可以保持简洁, 如下

.article-list {
  width: 100%;

  %__article-title {
    font-size: 20px;
  }

  &__article-title {
    @extend %__article-title;

    &--red {
      @extend %__article-title;
      color: red;
    }

    &--green {
      @extend %__article-title;
      color: green;
    }

    &--blue {
      @extend %__article-title;
      color: blue;
    }
  }

  &__article-subtitle {
    font-size: 16px;
  }
}

HTML

HTML需要注意的点如下

  • block中嵌套的子block也可以做为block使用
  • 没有block时, element不能直接使用
  • 用简单类来描述
block中嵌套的子block也可以做为block使用
<div class="category-list">
  <div class="category-list__category-title">
    分类1
  </div>
  <div class="article-list">
    <div class="article-list__article-title">
      标题
    </div>
    <div class="article-list__article-title">
      标题
    </div>
  </div>

  <div class="category-list__category-title">
    分类2
  </div>
  <div class="article-list">
    <div class="article-list__article-title">
      标题
    </div>
    <div class="article-list__article-title">
      标题
    </div>
  </div>
</div>

在上述HTML代码中,block article-list 作为 block category-list 的子block, 这样使用完全没有问题。
由于1个block对应1个SCSS文件, article-listcategory-list 在定义样式时就可以完全不相互嵌套而保持独立。

没有block时, element不能直接使用

假设有SCSS代码如下

.hoge-block {
  width: 5000px;

  &__link {
    color: blue;
    cursor: pointer;
    text-decoration: underline;
  }
}

这时, 如果在不是hoge-block的子节点位置使用hoge-block__link的话,则违反了规则

<div class="article-list">
  <!- 这里class名应该为hoge-block而不是article-list! ->
  <a class="hoge-block__link">
    跳转链接
  </a>
</div>

作为BEM优点之一,就是容易预测SCSS里所定义的样式所影响的HTML哪些范围。如果采用上述写法, 将会使代码丧失这个优点, 也间接导致了维护hoge-block__link的成本增加。所以 hoge-block__link使用时,必须在hoge-block 之内。

用简单类来描述

如果使用多个类来指定hoge-block, 也是违反了规则。如下

<div class="article-list hoge-block">
  <a class="hoge-block__link">
    跳转链接
  </a>
</div>

正确的写法如下

<div class="hoge-block">
  <div class="article-list">
    <a class="hoge-block__link">
      跳转链接
    </a>
  </div>
</div>
  • hoge-block的内部使用hoge-block__link 并不违反规则
  • 但是,没有解决hoge-block__link出现在article-list的内部
  • 从长远来看, 这也会让SCSS维护成本变高

知识点

  • block单位和大小比较自由
  • 不要使用block的modifier
  • 不要指定block的margin
    • 只有block和element的多类名是允许的
  • 通过block属性将公共样式提取到专用文件中
block单位和大小比较自由

对于一个BEM新手来说, 从哪里开始到结束来定义一个block是个经常被问到的问题。

block的大小一般由以下2个判断基准来决定

  • 不止是1个页面, 多个地方使用到的UI元素, 则把它定义成block
  • 当上面基准不满足的情况下,为了保持1个block不会太大而粗略地将它定义成block

只要遵循着前面的规则来设计, 一个大的block可以很容易地拆分成2个小的block. 可以基于是否可重复利用的部分重构block

不要使用block的modifier

虽然BEM设计原则中,提到可以使用block的modifier。 但在SCSS中,如果使用block的modifier, 需要考虑的量就变得很大, 也会导致SCSS变得复杂化。
基于考虑block的modifier并不推荐使用

不要指定block的margin

假如有个用户页面包括朋友列表, 照片列表, 文章列表。代码如下

<div class="user-page">
  <div class="friend-list">
    <!- 省略 ->
  </div>
  <div class="image-list">
    <!- 省略 ->
  </div>
  <div class="post-list">
    <!- 省略略 ->
  </div>
</div>

对应的CSS为

.user-page {
}

.friend-list {
  margin-bottom: 10px;
}

.image-list {
  margin-bottom: 10px;
}

.post-list {
  margin-bottom: 10px;
}

在前面提到的定义block基准中有不止是1个页面, 多个地方使用到的UI元素, 则把它定义成block这么一条。
block friend-list 中的margin-bottom: 10px; 是以用户页面为前提,当在用户页面以外调用此样式名称时,可能就会产生排版样式问题。
所以在block里指定margin时,会使block的通用性降低

只有block和element的多类名是允许的

为了解决上面问题, 可以改写HTML代码如下

<div class="user-page">
  <div class="user-page__list friend-list">
    <!- 略 ->
  </div>
  <div class="user-page__list image-list">
    <!- 略 ->
  </div>
  <div class="user-page__list post-list">
    <!- 略 ->
  </div>
</div>

样式代码如下

.user-page {
  &__list {
    margin-bottom: 10px;
  }
}

.friend-list {
}

.image-list {
}

.post-list {
}

通过这种写法,可以保持各个block的通用性,也实现了margin.

通过block属性将公共样式提取到专用文件中

当想统一定义网站所有链接样式,如果在各个block, element中复制样式,会导致代码变得难以维护。
这个场合,通过placeholder selector将链接的样式提前定义,各个block使用时再import进来,通过extend来实现。

  • global.scss
%link {
  cursor: pointer;
  text-decoration: underline;
}
  • article-list.scss
@import "global";

.article-list {
  &__link {
    @extend %link;
    font-size: 16px;
  }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注