BEM规范 | 电商订单模块UI设计实战-- CSS篇(一)

551 阅读7分钟

写在前面

最近一直在练习JS剑法有关的知识,有段时间没有碰CSS这把剑了。恰好有幸了解到BEM(Block Element Modifier)命名规范的相关内容,本来没在意,但我发现日常使用的微信的界面UI,居然也遵循了这一规范。

image.png

乱是乱了点,但这确实是WeUI,里面还展示了许多组件。那这个BEM规范是否值得好好学习一番呢?不管了,我先冲了!我花了点时间模仿淘宝写了一个电商的订单模块,先贴张效果图。

image.png

图标和样式参考的都是淘宝的界面,图标库我会在文末给出,接下来就通过这个小例子来体会一下BEM规范。

什么是BEM?

BEM — 是一种帮助开发者在前端开发中创建可重用组件和代码共享的方法。

通俗地说,BEM(Block )就是对CSS代码编写的一种约定。其中:

  • B(Block)为块元素,代表了本身有意义的一种封装实体,块与块之间可以嵌套使用。

    命名:<div class=".block"></div>

  • E(ELement)为子元素,是块的一部分,其本身没有意义,但与其所关联的块在语义上有关系。

    命名:<div class=".block__element"></div>

  • M(Modifier)为修饰符,修饰块元素或子元素,也就是说其可以与块或者子元素搭配使用,改变它们的状态。 命名:<div class=".block__element--statu"></div>

    下面给出官网的例子:

    <style>
      .form { }
      .form--theme-xmas { }
      .form--simple { }
      .form__input { }
      .form__submit { }
      .form__submit--disabled { }
    </style>
    <body>
        <form class="form form--theme-xmas form--simple">
            <input class="form__input" type="text" />
            <input
              class="form__submit form__submit--disabled"
              type="submit" />
        </form>
    </bofy>
    

    这里解释一下,上面的.form是一个块元素,代表一个表单实体,是单独的组件。.form__input.form__submit分别代表了表单的输入框和提交元素,它们与块元素——表单使用双下划线__连接,表示这些是块元素的直接组成部分。对于.form__submit--disabled,按照之前的介绍我们可以知道这表示了一个修饰符,也就是在表单form下,提交元素submit的状态statu

    值得注意的是,上述代码中还有form--theme-xmas form--simple这种更为直接的结构,将块与修饰符直接使用--连接。注意,块和元素都能连接修饰符,因为它们都有单独的状态

    不过这里似乎还有一个坑,也就是大家注意到的-,这个单横线代表什么呢?难道也表示什么状态?一开始我也有点疑惑,明明官方文档中没有给出单横线-的解释,这里却使用了,这有什么用呢?

    我想告诉大家的是,这个单横线-确实不属于BEM命名规范,因为form--theme-xmas form--simple表示的是表单中使用主题元素,主题风格为圣诞风格,这个圣诞风格并没有被定义为一种修饰符,而只是一种易于区分的标记,将form--theme-xmas看作是一个整体,它与上述的form--simple类型其实是一样的,只不过前者人为加了一个修饰符的后缀(标记),这个点容易先入为主。

结论: 能够单独存在,对块元素有整体影响的名词,比如:theme应该作为一个修饰符,使用--连接。而,依赖于块,离开块就失去其意义,且只对块的局部有影响的,比如:input元素自身,应当作为子元素,使用__连接。

对于新手来说,区分子元素和修饰符是一个难点,掌握了这个就不会再诟病BEM规范复杂了。

实战

接下来我们一起来看一下实战中如何运用BEM规范。

fd7691c6f67c0a798de8bae0f924473.png

还是这张图,这里我参照了WeUI的命名方式,命名规范还是严格遵循BEM官方文档。分为 .order__hd和**.order__bd**两个部分,其中.order__hd又可以分为:.order__title.order__desc,而.order__bd中:若干个.order__item。至于上面一栏功能,都是.function__bd中的.function__item。我们还是来看看HTML部分代码:


    <div class="ds">
    
      <div class="ds-function">
        <div class="ds-function__hd"></div>
        <div class="ds-function__bd">
          <a href="#" class="ds-function__item">
            <span class="ds-function__icons ds-function__icons--star">
              <svg>...</svg>
            </span>
            <span class="ds-function__name ds-order__name-star"> 收藏 </span>
          </a>
        </div>
      </div>
      
      <div class="ds-order">
        <div class="ds-order__hd">
          <div class="ds-order__title">我的订单</div>
          <div class="ds-order__desc">全部 ></div>
        </div>
        <div class="ds-order__bd">
          <a href="#" class="ds-order__item">
            <span class="ds-order__icons ds-order__icons--unpay">
              <svg>...</svg>
              <span class="ds-order__count ds-order__count--unpay">8</span>
            </span>
            <span class="ds-order__status ds-order__status--unpay">
              待付款
            </span>
          </a>
        </div>
      </div>
      
    </div>

这里包括了:上部的收藏图标示例,下部订单以及图标的示例,其他图标都是如法炮制。单独揣摩下这部分:

<div class="ds-order__bd">
    <a href="#" class="ds-order__item">
      <span class="ds-order__icons ds-order__icons--unpay">
        <svg>...</svg>
        <span class="ds-order__count ds-order__count--unpay">8</span>
      </span>
      <span class="ds-order__status ds-order__status--unpay"> 待付款 </span>
    </a>
<div>

其中,ds代表整个电商模块,我在<a>标签内定义整个.order__item,放置图标,在图标右上角设置待付款订单的数量,最后在图标底部显示待付款字样。

.order是块元素,代表订单。

.order__bd是订单的子元素,代表订单界面的主体元素—主体内容。

.order__item也是订单的子元素,代表订单界面里面的个体元素—订单项,包含于主体元素.order__bd

.order__icons、.order__count和.order__status三者都是隶属于.ds-order__item,其中.order__count包含于.order__icons。

.ds-order__status--unpay是一个修饰符,用于表示订单的状态具体为"未付款"。

继续看CSS部分的代码:

<style>
      * {
        margin: 0;
        padding: 0;
        outline: none;
      }

      body {
        font-family: system-ui, -apple-system, sans-serif;
        background-color: #f1cece;
        height: 100vh;
      }

      a {
        /*取消下划线*/
        text-decoration: none;
        color: #000;
      }

      .ds {
        background-color: #ededed;
        border-radius: 15px;
      }

      .ds-order {
        background-color: rgb(255, 255, 255);
        border-radius: 15px;
        margin: 0 8px;
      }
      
      .ds-order__hd {
        height: 40px;
        display: flex;
        align-items: center;
        justify-content: space-between;
      }

      .ds-order__bd {
        height: 80px;
        display: flex;
      }

      .ds-order__title {
        font-family: 微软雅黑;
        padding-left: 10px;
        font-weight: 600;
      }

      .ds-order__desc {
        font-size: 14px;
        color: grey;
        padding-right: 10px;
      }

      .ds-order__item {
        display: flex;
        height: 100%;
        width: 100%;
        flex-direction: column;
        align-items: center;
      }

      .ds-order__icons {
        margin-top: 10px;
        position: relative;
      }

      .ds-order__count {
        width: 10px;
        padding: 0px 5px;
        border-radius: 50%;
        background-color: transparent;
        position: absolute;
        top: -5px;
        right: -5px;
        z-index: 1;
      }

      .ds-order__count--unpay {
        background-color: #ff8800;
      }

      .ds-order__count--send {
        background-color: #fff;
      }

      .ds-function__bd {
        display: flex;
        height: 80px;
      }

      .ds-function__item {
        display: flex;
        height: 100%;
        width: 100%;
        flex-direction: column;
        align-items: center;
        justify-content: center;
      }
    </style>

flex-direction: column这里是将一列上的元素对齐,其他样式都不难,主要是先把结构搭好调整可以慢慢来。

分析清楚基本的结构层次,我们最后来谈谈基于BEM规范设计这种页面的难点:

  • 难点一:命名,命名在BEM规范中是很重要的,命名的准确度直接影响了代码的层次性,命名的好坏体现了我们的规范程度。这个案例我参照了WeUI官方的结构。完整的WeUI页面结构通常包括三部分:page__hd、page__bd和page__ft,也就是页面的头、身体和尾部。

  • 难点二:明确作用域,每个块、元素和修饰符应该规划好合理的边界,这需要我们更多地在项目上遵循这一规范,熟练使用就能掌握。也是参照着WeUI的页面命名,我快速理清了本案例的结构。

  • 难点三:类名冗长,这是BEM追求规范化和明确化需要付出的代价,这个时候需要我们学会利用Sass、Less等CSS预处理器的特性,如@extend@include等,减少代码的重复,但这也需要我们花一些时间掌握。

BEM规范的优点

  • 提高代码的可维护性:BEM规范使得项目开发模块化,也就是结构层次清晰,每个组件样式相对独立,减少了代码之间的耦合度。

  • 提高代码的复用性:BEM官方鼓励我们向组件开发,一个完整的块结构在很多情况下可以轻易在别的项目复用,降低了开发成本。

  • 易于拓展:在一个BEM高手的眼中,功能的改变只是基于块__元素--修饰符的CRUD,这种有套路的设定让一切变得更加简单。

总结

BEM — 一种帮助开发者在前端开发中创建可重用组件和代码共享的方法。包括块(block)、(element)元素和(modifier)修饰符,其中元素之间可以嵌套。

以上就是我在实战中学到的。在学习BEM规范过程中,我也被这种命名难度和阅读难度给整的很混乱。但是我认真梳理了一下,就发现BEM规范之所以在WeUI这些项目中被广泛应用,就是因为它在越复杂的项目中,所凸显的优势就越大。

由于笔力有限,关于BEM规范的许多热点问题请各位看官移步官网(getbem.com/)。

参考:
1.BEM — Naming (getbem.com)
2.iconfont-阿里巴巴矢量图标库

本人拙见,若有错误,敬请指正。