结合SEM和BIO来改进CSS

发布于 大漠

特别声明,本文来源于@RYAN YU的《Combining the Powers of SEM and BIO for Improving CSS》一文。

有人可能会说,CSS很简单,但是“容易”会导致代码混乱。在大家的眼里,使用像Sass或LESS这样的处理器会让事情变得更为简单。但事实上并不一定如此,如果你使用的不小心,你的CSS将变得更难处理,而不是更易处理。Sass?困难吗?Sass的嵌套就展示了这一点,使用不当,Sass的嵌套就是地狱。

如果你的Sass代码看起来像这样的,那么你肯定可以使用SEM和BIO来改进你的代码。接下来我就将向你介绍这方面的CSS技术。

在本文中,我将使用下面的代码示例来解释SEO和BIO是如何工作的,以及它们如何有助于增强CSS,如何更好的管理你的CSS。

通常,SEM关注的是CSS的高级技术,而BIO是帮助你编写更好的CSS,以实现SEM的实际技术。SEM和BIO的主要目的是更好地处理CSS的权重,这是你应该理解的CSS中最重要的概念之一。

首先,我们来聊聊SEM。

SEM

SEM是ScalableExtensibleMaintainable三个单词的首字母的缩写。其意思是可伸缩的可扩展的可维护的意思。

尝试这样做肯定可以改进你的CSS代码,并帮助你构建更可靠的组件。让我们详细讨论一下这方面的细节。

可伸缩的(Scalable)

可伸缩的(或可重用的)组件意味着应该在不进行任何编码的情况下,在任意需要的地方使用外观相同的组件。

从上面的Codepen示例中,我们不难发现,在顶部(头部)中的搜索中的按钮(button)看上去和侧边栏的链接按钮完全相同。当我们比较HTML标记时:

  • 搜索按钮使用的是<button>元素
  • 但链接按钮使用的是<a role="button" ...>元素

虽然两者使用的标签元素不同,但它们使用了相同的类名:.c-btn.c-btn--yellow

按钮样式是伸缩的,它允许你在任何地方添加外观相同的组件,因为它不会被它的父元素或兄弟元素污染。让你不需要为完全不相关的组件被破坏而头疼,即使在完全不同的位置被另一组件调用。

可扩展的(Extensible)

可扩展的组件可以很容易地提供附加的特性(或功能),而不需要破坏自身而重新编写代码。比如下面的示例:

除了3D效果外,头部和主内容区域中的按钮看起来非常相似。在这种情况下,我们可以通过扩展基础按钮来实现3D按钮效果,而不需要创建完全不同代码的两组按钮。

页脚上的按钮也是如此。即使按钮有不同的颜色和大小,我们也可以通过添加或删除新的或不同的特性来扩展它。

可维护的(Maintainable)

对于大多数前端来说,最大的挑战之一可能是理解其他人或我们自己过去写的CSS。我们有时要花不少的时间去理解现有的代码,而不是添加新的代码。

主要问题来自于:

  • 代码中没有任何注释
  • 过度的系统设计
  • 没有真正的来源
  • 没有考虑编码的标准(或最佳实践)

有了SEM和BIO,我们就可以改进代码并将其他人(包括我们自己)从混乱的、不可维护的代码中拯救出来。

BIO

有很多很好的技术可以改进我们写CSS的方式,根据我的经验,我发现下面三种方式就是编写CSS的很好方式之一。他们首字母缩略就组成了BIO

  • BEM
  • ITCSS
  • OOCSS

很多开发人员(或工程师)知道这些编写CSS的技术,但是我想一一介绍并讨论我使用这些技术的方式。

BEM

BEM是非常流行的方法,它一直在帮助我们显著地改进我们对CSS和Sass(或LESS)的思考方式。BEM其实是下在三个词的缩写:

  • Block
  • Element
  • Modifier

正如上面的示例所显示的那样,很多开发人员过度的使用Sass(或LESS)的力量,并陷入了嵌套的魔咒之中。但是在BEM中,通过在一个(或两个)嵌套层上写样式,保持一个非常低的CSS权重。

如果你曾经因为CSS选择器权重而烦恼,那么使用BEM是多爽。

回到我们的示例,HTML的标记如下所示:

<div class="o-grid">
    <div class="o-grid__item o-grid__header">
        ...
    </div>
    <div class="o-grid__item o-grid__main">
        ...
    </div>
    <div class="o-grid__item o-grid__sidebar">
        ...
    </div>
    <div class="o-grid__item o-grid__footer">
        ...
    </div>
</div>

示例包括:

  • 一个区块:.o-grid
  • 元素:.o-grid__item, .o-grid__header, .o-grid__main, .o-grid__sidebar, .o-grid__footer

由于BEM提供了一种命名约定,强调唯一的类,所以我们不必像下面这样使用多层的嵌套:

.o-grid {
    .o-grid__item {
        ...
    }
}

相反,我们可以用更少的层次来定义它的样式:

.o-grid__item {
    ...
}

这就是BEM最大的优点:降低CSS选择器权重,提高整个CSS编码的效率和体验。

即使是BEM,我偶尔也会遇到一个问题,那就是不好命名。如果你没有足够的注意力,你可能会得到一个很长的类名:

.o-grid__item-search-button-text-svg-icon {
    ...
}

当你创建一个类名时,使用BEM的核心概念:你的组件是一个块,其中的所有元素都单独地附加到这个块上。

在我们的示例中,使用.o-grid__form替代.o-grid__item-form,因为该表单本身是一个单独的组件,不需要与o-grid__item绑定并成为其子元素。

另外,为了更有效地控制样式,我添加了另一个类名o-grid__headero-grid__item来扩展样式。此外,该按钮包含使用OOCSS方法的BEM样式的类,接下来将介绍这一部分。

OOCSS

正如我们已经讨论过的,有很多很棒的CSS方式可以帮助我们改进CSS编写方式。然而,我看到很多人强迫息决定用一种方法。

根据我的经验,结合方法论,我们编写CSS的时候实际上可以结合多种方法的优势来提高编写代码的效益。例如,我个人发现BEM和OOCSS在一起就是一种很好的组合方式。

OOCSS代表面向对象的CSS,你可以把它想像成乐高积木:

OOCSS分创建很多个单独部分,然后将它们组合在一起,以便构建组件。从我们的示例中,使用OOCSS命名创建了按钮:

  • .c-btn
  • .c-btn--yellow
  • .c-btn--blue
  • .c-btn--3d
  • .c-btn--large

在示例中,头部中搜索的按钮,将使用下面的类组合在一起构建的:

  • .c-btn
  • .c-btn--yellow

如果我们想要在主要内容区域中创建一个3D的按钮,我们要添加一个3D按钮的类.c-btn--3d

对于页脚中的蓝色的按钮,我们可以将黄色修饰符换成蓝色的修饰符,并添加大按钮的修饰符。正如你所看到的,该按钮不依赖于头部区域,这使我们可以更灵活使用和重用组件。通过这样做,我们可以在不影响任何其他组件或模块的情况下构建按钮,同时可以轻松地扩展新的表现特性,比如颜色和形状。

在这里,使用OOCSS创建按钮集合来创建变体的示例:

在BEM和OOCSS的基础上,在ITCSS的帮助下,我们可以进一步改进我们的CSS编写。下面我们来看看ITCSS这个方法。

ITCSS

ITCSS代表的是倒三角CSS(Inverted Triangle CSS),它通过应用一个结构来帮助我们组织CSS代码,这个结构可以确定使用特定组件的具体程序。@Lubos Kmetko写了一篇有关于ITCSS很好的文章,非常值得一读。

你也可以看看我是如何使用ITCSS的,将样式按特定的程度分组是其重要的特点。

基于这个示例,你可以通过向类添加命名空间来查看如何命名组件。因此,比如按钮组件的前缀可以用c.c-button),以指示组件的状态,并防止将其误认为另一项。通过这样做,项目中的每个人都知道这个特定类的功能,以及更改它的属性可能会影响其他的领域。

下图显示所有ITCSS级别:

让我们来看看每个部分。

设置(Settings)

设置通常是不生成CSS的变量集合,而是应用于类。其主要包括:

  • 基本(Base)
  • 颜色(Color)
  • 排版(Typography)
  • 动画(Animation)

工具(Tools)

工具也不会产生任何CSS,并且通常是预处理函数,帮助在类上编写或扩展属性:

  • 函数(Functions)
  • 占位符(Placeholders)
  • 混合宏(Mixins)
  • 媒体查询(Media Queries)

供应商(Vendors)

供应商是在项目中使用的第三方样式。比如reset.cssnormalize.css,甚至是Foundation和Bootstrap这样的CSS Frameworks等。

这些样式在结构中较高的原因是,如果我们需要,还可以重写它们。你还记得,如果同一个类被调用两次,比如说两属性完全相同,那么级联将呈现第二个属性。

.btn--large {
    padding: 3em;
}

/* 将覆盖第一个 */
.btn--large {
    padding: 5em;
}

顺便提一下,在Sass中,你可以使用~指向node_modules文件夹,这样你就可以从源代码中导入样式,而不必将其移动到自己的项目目录中。

@import '~modern-normalize/modern-normalize';

对象(Objects)

对象(命名空间以o-开头)用于设计模式。所有页面都使用对象类,因此如果对象类做任何更改,都应该非常小心,因为任何更改都会影响整个网站的每个页面。

我最常用的对象类是:

  • .o-page:最外层的容器,比如在容器中运用max-width: 100vwoverflow:hidden
  • .o-main:主内容区域的外部容器
  • .o-container:组件的外部容器,通常提供固定的宽度
  • .o-content:实际内容区域的任何额外配置
  • .o-grid:网格布局

如果你有使用其他对象类,欢迎与我一起分享。

元素(Elements)

元素(命名空间以e-开头)是HTML元素,我们不会根据类名对其进行样式化。例如,我们为<a>元素提供默认样式,而不是添加的类名.link

// 链接默认样式
a {
    text-decoration: none;

    &:hover {
        background-color: blue;
        color: white;
    }
}

// 不要为链接添加类并提供样式 
.link {
    text-decoration: none;

    &:hover {
        background-color: blue;
        color: white;
    }
}

这是因为,特别是在WordPress这样的CMS系统中,你不会希望每次在内容中使用链接时都添加类。因此,我们为<a>元素提供了一个默认样式,因此没有任何类,链接仍然具有好看的样式。

组件(Components)

组件(命名空间以c-开头)是构成网站一部分的一个小特性。比如buttonaccordionsslidersmodal等。每个组件都是完全独立的,不依赖于任何其他组件。当你命名组件时,应该考虑这个事实。

比如,前面示例中的主内容区域中的按钮不应该被称为c-main-button,因为main将它限定在主内容区域中,并限制在其他地方(比如侧边栏)中使用它。像.c-btn这样的东西会更好,因为按钮不再绑定到页面的其他特定部分。

如果你需要扩展一些其他的特性,你可以使用BEM修饰符扩展属性,或者使用作用域。

模式(Patterns)

许多开发人员(或者工程师)使用的组件术语和模式差不多一个意思,如果你更认同这样的一个概念,也完全没有问题。我只是想把这两部分分开来说。

对于一般的经验法则,我认为模式(命名空间以p-开头)是组件的组合,但在某种程度上是不何扩展的组件。

例如,我将手风琴(accordion)作为一个组件。它本身是可伸缩和可重用的,这也意味着它可以在网站的其他区域中使用,即使手风琴会包含其他组件,比如按钮。

另一方面,比如,页头也是一个模式,因为它是不可伸缩的(页头不能在主内容或侧边栏区域中使用),而且也还包含基他组件,比如按钮、手风琴、菜单,微标和搜索表单等。

作用域(Scope)

注意,我只在有必要的情况下才使用作用域。作用域(命名空间以s-)开头,其目的是为我们提供最高的权重,以例我们可以为特定的目的覆盖任何样式。

请记住,如果你多次使用作用域类,你可能会编写过于具体的样式,你应该考虑重构你的CSS或HTML。

这里有一个使用作用域的类.s-home

.c-accordion {
    .s-home & {
        // 改变主页上手风琴的背景颜色 
        background-color: tomato;
    }
}

补充一点,可以给手风琴提供修饰符(例如:.c-accordion--bg-tomato)替代作用域的类来得构上面的代码。这将是一种更可扩展的方式,使用组件更加模块化。

扩展命名空间

除了上面讨论的命名空间之外,我还经常使用另外两个命名空间:

  • is-:用来表示块或元素的状态。最常用的类是.is-active,就像导航中当前状态的链接
  • js-:用来表示特定的元素被绑定到JavaScript事件。例如,js-menu-click表示元素绑定了click事件

监测(Linting)

最后,使用.stylelint.eslint制定的规则可以显著提高代码质量。

在前端的工作流中,我不把它作为推荐。在我的工作流中,这是强制的,如果你平时工作中不遵守的话,是通不过的。

通过这种方式,我们可以确保代码质量,并让你的代码保持在最佳状态,并为其他开发人员(包括你未来的自己)提供更好的代码。

实战

在本节中,我将讨论如何使用SEM和BIO。我已做了一个简单而实用的例子。我们就从这个示例开始:

这个示例主要做了一个手风琴,它可以被用作:

  • 一种普通的手风琴,但在主内容区域有不同的颜色
  • 侧边栏中的菜单
  • 在页脚显示社交媒体图标的区块

我们取得的成果是:

  • 可伸缩:因为它可以添加到页面的任何部分,而不需要修改任何代码
  • 可扩展:因为它可以在不改变核心功能的情况下服务于不同的功能
  • 可维护性:因为它的组织方式是有意义的

在示例中使用SEM和BIO的特性主要包括:

  • BEM.c-accordion作为一个块,它的子元素也使用了修饰符,比如.c-accordion--light.c-accordion--dark
  • ITCSS:Sass文件的排序很好处理CSS的权重。例如,在侧边栏中手风琴按钮包含了class="c-accordion__trigger p-sidebar-menu__button"。模式p-会在没有问题的情况下重写组件c-
  • OOCSS:手风琴是由几个类组成的,例如class="c-accordion c-accordion--dark c-accordion--single",每次只打开一个面板就会产生一个dark主题

总结

我几乎在我的所有项目中都使用了这种方法,包括大学、政府部门、商业零售商和许多其他网站。在每一个案例中,我都成功地将所有的项目交付给客户(在客户审批阶段几乎没有问题并按时交付);到目前为止,这种方法对我非常有效,我认为对你也是如此。

话又说回来,技术总是在变化的(特别是前端领域的技术),我很高兴能听到你的想法,更希望有机会能与你一起讨论。如查你有更好的想法或建议欢迎在评论中与我一起分享!

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/css/combining-the-powers-of-sem-and-bio-for-improving-css.htmlUA Air VaporMax 2019 White Black lace