PostCSS深入学习: 结合BEM和SUIT方法使用PostCSS

发布于 大漠

这篇教程我们将学习如何结合BEM和SUIT来使用PostCSS,让编写CSS样式更容易、更有效。

这两个方法都是用来给类命名的,使用它更容易让你将风格紧密结合在一起,并且帮助其他开发人员从各种的类名就能识别出对应的样式风格。

BEM是由@Yandex提出的一种类名命名方式。SUIT是基于BEM上的另一种类名命名方式,只不过@Nicholas Gallagher在BEM的基础上做了一些调整。BEM能做的事情,SUIT都可以做,但很多用户觉得SUIT是BEM的一种改进。

使用这些方法确实有助于产生更好的,理有结构化的CSS。然而,需要的注意的是,手动输入结构所需的类名会让你感到厌烦,并且跟踪这些类以及如何推动,更会让你感到头痛。

@Malte-Maurice Dreyer的postcss-bem插件通过缩写和嵌套缓解了这些问题。在接下来的教程中你将学习如何使用。

为了确保你能更好的学习或了解postcss-bem插件的好处和如何使用它,我们有必要先对BEM和SUIT做一个快速的了解。

BEM快速入门

Block

在BEM中Block是设计中的最高一级,整个网站都是由很多个块构建而成。一个块在网站上是独立的,理论上说块可以放置在你的布局的任何一个地方,甚至还可以嵌套在另一个块里面。

例如,在你的网站上的搜索表单块,你就可以使用.search-form类来表示。

Element

在BEM中的Element其实是Block中的一个元素。使用两个下划线__将元素的名称附加到其父块的名称后。

例如,一个搜索表单可能包括标题、文本框和提交按钮等元素,那么它们的类名可以命名成:.search-form__heading.search-form__text-field.search-form__submit-button

Modifier

在BEM中Modifier是应用于Block或Element表示改变的描述,或者是状态改变。修饰符名称一般附加在Block或Element的后面。

BEM的官方网站上,修饰符使用的一个_做为分隔符。但@Harry Roberts提供了一份类似于BEM的CSS指南规范中(把这种称为BEM-like方法),使用两个破折号--做为分隔符,而且这种方式比BEM官方提供的命名方式使用更为广泛。

例如,在设计中你可能希望提供一个高级搜索表单的样式不同于常规的搜索表单样式。因为创建了一个修饰符(Modifier)来作为区分:BEM官网的命名方式.search-form_advanced,BEM-like的命名方式:.search-form--advanced

比如例外一个例子,你希望给提交了一个无效内容,而此时你想要改变表单的外观状态。这样你也可以创建一个修饰符。.search-form_invalid(BEM官方),.search-form__invalid(BEM-like)。

SUIT快速入门

SUIT包括结构(Utilities)和组件(Components)。组件(Components)中又可以有修饰符(Modifiers)、后代(Descendants)和状态(States)。

SUIT使用Pascal命名法驼峰命名法和破折号。在BEM中,约定命名使用到的破折号和下划线的数量,常常给人带来困惑。例如,在BEM中的命名方式.search-form__text-field和SUIT的命名方式.SearchForm-textField

Utilities

Utilities是用来处理结构和位置方面的样式,组件中也可以用这种方式来写。常常在驼峰命名前加一个u-前缀。例如.u-clearFix

Components

SUIT中的Components就相当于BEM中的Block。组件的命名方式常常使用pascal命名,也更适合SUIT,使它们更容易识别。例如.SearcForm

组件命名空间

组件可以在命名前加一个nmsp-这样的命名空间,用来防止类名的冲突。比如.mine-SearchForm

Descendants

SUIT中的Descendants相当于BEM中的Element。它使用破折号-和驼峰命名结合在一起来。例如.SearchForm-heading.SearchForm-textField.SearchForm-submitButto

Modifier

SUIT中的Modifier和BEM中的一样,但SUIT对他们的角色控制的更为严格。SUIT中的Modifier只用于组件(Components)上,不用于Dedicated上。它也不能用于表示状态(State)变化,就算要用于状态的变化,SUIT也有自己一套专用的命名约定。

Modifier都用两个破折号--来连接,例如:SearchForm--advanced

State

State是用来反映组件更改的状态。通过不同的修饰词,反映出组件修改后的基本外观。如果有必要,State也可以应用于Descendent中。

State一般都在驼峰命名前添加is-前缀。如:.SearchForm.is-invalid

项目配置

如果SUIT和BEM是你的工作中的必需品,那时候设置你的项目了。

你需要做的第一件事情是使用Gulp或Grunt设置你的项目。如果你没有一个较好的项目模板,你可以使用Gulp或者Grunt使用最少的代码来达到相同的目的。

你可以阅读前面有关于PostCSS的教程,了解有关于如何使用Gulp或Grunt设置您的项目:

如果你不想从头开始手动设置您的项目,你可以下载本教程中提供的源码附件,提取Gulp或Grunt项目到一个空的文件夹中。

然后在命令终端运行:npm install

安装插件

接下来,你需要把postcss-bem插件安装到你的项目中,为了让它能更好的工作,我们还需要安装一个postcss-nested插件。

不管你使用的是Gulp还是Grunt,运行下面的命令,将插件安装到你的项目文件夹中:

npm install postcss-bem postcss-nested --save-dev

好,现在已经把插件安装到你的项目中了。

通过Gulp加载插件

如果你使用的是Gulp,那么可以在gulpfile.js文件中添加下面的变量:

var bem = require('postcss-bem');
var nested = require('postcss-nested');

将新添加的变量添加到processors数组中:

var processors = [
    bem,
    nested
];

命令行中运行gulp css命令,做一个快速测试。这个时候你可以看到dest/文件夹中添加了一个style.css文件。

通过Grunt加载插件

如果你使用的是Grunt,更新gruntfile.js文件中的processors对象,并且给对象嵌套一个options

processors: [
  require('postcss-bem')(),
  require('postcss-nested')()
]

在命令行中运行grunt postcss做一个快速测试,你会发现在你的项目中的dest/文件夹中添加了一个新文件style.css

好了,你现在都已准备好了。我们接下去就可以学习如何在项目是生成BEM和SUIT结构。

postcss-bem结合BEM和SUIT使用

手工编写BEM和SUIT结构是很笨拙的开发方式,不断的重复声明相同的类名会变得无聊,而且跟踪起来也很容易混淆。

当你使用postcss-bem就会变得非常的容易,重复的输入类名就几乎不存在。

生成SUIT结构

默认情况之下postcss-bem会根据SUIT语法输出,而不是BEM。不过也可以输出BEM语法,稍后我们会介绍。但插件主要还是用来输出SUIT语法,出于这个原因,我们先从SUIT的语法说起。

生成一个组件(Component)

创建一个组件,可以使用@component ComponentName{...}语法。

src/style.css文件中尝试添加一个SearchForm组件:

@component SearchForm {
    padding: 0;
    margin: 0;
}

编译出来的结果是:

.SearchForm {
  padding: 0;
  margin: 0;
}

生成一个Descendent

通过@descendent descName{...}语法在组件中嵌套,可以创建一个Descendent。

SearcForm组件中嵌套一个textField,添加一个Descendent,如下所示:

@component SearchForm {
    padding: 0;
    margin: 0;
     
    /* Nest descendent under component */
    @descendent textField {
        border: 1px solid #ccc;
    }
 
}

编译出来是这样的:

.SearchForm {
    padding: 0;
    margin: 0;
}
 
.SearchForm-textField {
    border: 1px solid #ccc;
}

生成一个Modifier

通过@modifier name{...}嵌套在组件内给组件创建一个修饰符Modifier。Modifier通常放在组件的最顶部,他高于任何一个Descendents和States。

SearcForm组件中添加一个名叫advanced的Modifier:

@component SearchForm {
    padding: 0;
    margin: 0;
     
    /* Typically, place modifiers above descendents */
    @modifier advanced {
        padding: 1rem;
    }
     
    @descendent textField {
        border: 1px solid #ccc;
    }
}

重新编译你的代码,你会看到Component中添加了一个advanced的Modifier:

.SearchForm {
    padding: 0;
    margin: 0;
}
 
.SearchForm--advanced {
    padding: 1rem;
}
 
.SearchForm-textField {
    border: 1px solid #ccc;
}

生成一个State

通过@when name {...}语法嵌套在一个组件(Component)或一个Descendent中可以创建一个State。

textField添加一个invalid,给textField的Descendent添加一个名为invalid的State:

@component SearchForm {
    padding: 0;
    margin: 0;
     
    @modifier advanced {
        padding: 1rem;
    }
     
    @descendent textField {
        border: 1px solid #ccc;
         
        /* This creates a state for the textField descendant */
        @when invalid {
            border: 1px solid red;
        }
    }
 
}

编译出来的代码会包括一个名为invalid的State:

.SearchForm {
    padding: 0;
    margin: 0;
}
 
.SearchForm--advanced {
    padding: 1rem;
}
 
.SearchForm-textField {
    border: 1px solid #ccc;
}
 
.SearchForm-textField.is-invalid {
    border: 1px solid red;
}

组件全名空间

通过@component-namespace name {...}可以给组件(Components)创建命名空间,而且Descendents,Modifiers和States都嵌套在里面。如果你喜欢,整个样式表都可以采用命名空间,而且可以自动添加到类的前面。

使用@component-namespace mine{...}给容器添加一个命名空间:

@component-namespace mine {
 
    @component SearchForm {
        padding: 0;
        margin: 0;
         
        @modifier advanced {
            padding: 1rem;
        }
         
        @descendent textField {
            border: 1px solid #ccc;
             
            @when invalid {
                border: 1px solid red;
            }
        }
 
    }
 
}

编译出来的代码,会在你的组件前添加一个mine-前缀。如:

.mine-SearchForm {
    padding: 0;
    margin: 0;
}
 
.mine-SearchForm--advanced {
    padding: 1rem;
}
 
.mine-SearchForm-textField {
    border: 1px solid #ccc;
}
 
.mine-SearchForm-textField.is-invalid {
    border: 1px solid red;
}

生成一个Utility

使用@utility utilityName{...}创建Utility。你可能还记得,在创建你的项目时,已经安装了postcss-nested插件。我们这样做,是因为我们使用嵌套能更好的和postcss-bem达到一致效果,如下面的的例子,我们创建了一个clearFix:

@utility clearFix {
    &:before, &:after {
        content: "";
        display: table;
    }
    &:after {
        clear: both;
    }
    /* If supporting IE 6/7 */
    *zoom: 1;
}

编译之后,你可以看到下面的代码:

.u-clearFix {
    /* If supporting IE 6/7 */
    zoom: 1;
}
 
.u-clearFix:before, .u-clearFix:after {
    content: "";
    display: table;
}
 
.u-clearFix:after {
    clear: both;
}

生成BEM结构

在你的gulpfile.jsgruntfile.js文件中设置style:'bem',可以指定postcss-bem插件输出代码是按BEM的语法。

/* Gulpfile */
var processors = [
    bem({style: 'bem'}),
    nested
];
     
/* Gruntfile */
processors: [
    require('postcss-bem')({style: 'bem'}),
    require('postcss-nested')()
]

默认情况之下postcss-bem插件将使用一个下划线_。更重要的是根据你的项目,你也可以使用两个破折号--做为分离器。你也可以修改node_modules/postcss-bem文件夹下的index.js文件,修改第15行代码,你就可以重新配置postcss-bem插件的分离器符号:

bem: {
    separators: {
        namespace: '--',
        descendent: '__',
        modifier: '_'
    }
}

修改成:

bem: {
    separators: {
        namespace: '_',
        descendent: '__',
        modifier: '--'
    }
}

生成一个Block

BEM中的Block就相当于SUIT中的Component。使用@component block-name{...}就可以生成一个Block。

添加下面的代码就可以创建一个search-form的Block:

@component search-form {
    padding: 0;
    margin: 0;
}

编译出来的代码如下:

.search-form {
    padding: 0;
    margin: 0;
}

生成一个Element

BEM中的Element就相当于SUIT中的Descendent。你可以使用@descendent element-name{...}嵌套在Block中,创建一个Element。

你可像下面的代码一样,创建一个名为text-field的Element:

@component search-form {
    padding: 0;
    margin: 0;
     
    @descendent text-field {
        border: 1px solid #ccc;
    }
 
}

编译之后,你就能看到一个新的Element就创建了:

.search-form {
    padding: 0;
    margin: 0;
}
 
.search-form__text-field {
    border: 1px solid #ccc;
}

生成一个Modifier

尽管BEM允许给Block和Element创建一个Modifier,但postcss-bem插件只会处理嵌套在Block中的Modifier,而不会创建嵌套在Element的Modifier。这主要是因为Component有Modifier,而Descendent没有Modifier。可以通过@modifier name{...}语法,将Modifier嵌套在它的父块中。

可以像下面的代码一样,给search-form添加一个advanced的Modifier:

@component search-form {
    padding: 0;
    margin: 0;
         
    @modifier advanced {
        padding: 1rem;
    }
     
    @descendent text-field {
        border: 1px solid #ccc;
    }
 
}

编译出来的代码如下:

.search-form {
    padding: 0;
    margin: 0;
}
 
.search-form_advanced {
    padding: 1rem;
}
 
.search-form__text-field {
    border: 1px solid #ccc;
}

没有Utility和State,但有命名空间

在BEM模式中没有@utility@when语法,它们不会编译成任何东西,所以在BEM中不要使用Utility或State。

然而,尽管他们不属于BEM中的一部分,但@component-namespace语法仍然能工作,也能使用到你的样式表中。它将给你的类名添加一个类似name--的前缀。

.mine--search-form {
    padding: 0;
    margin: 0;
}
 
.mine--search-form_advanced {
    padding: 1rem;
}
 
.mine--search-form__text-field {
    border: 1px solid #ccc;
}

做个总结

现在你知道如何生成BEM和SUIT的结构,并且使整个过程更容易。让我们来总结一下:

  • BEM和SUIT是面向类名的一种规范,帮助你更好的组织样式,以及更好的帮助其他开发人员能识别各种类的目的
  • SUIT就像BEM,但额外的做了一些调整
  • postcss-bem插件提供了创建BEM和SUIT的类,如@component@descendent@modifier
  • 插件还允许代码嵌套,如Modifier嵌套在Component或Block中
  • 使用@component-namespace name{...}可以自动给容器创建命名空间

下一节

在下一篇教程中,将会看到使用PostCSS的更好方法,那就是通过一个提示工具,让我们编码更快更有效,而这个工具包装了速记(Shorthand)和一些快捷方式。如果您对这方面的知识感兴趣的话,欢迎持续关注这个系列教程的相关更新。

本文根据@Kezz Bracey的《Using PostCSS with BEM and SUIT Methodologies》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://webdesign.tutsplus.com/tutorials/using-postcss-with-bem-and-suit-methodologies--cms-24592

大漠

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

如需转载,烦请注明出处:https://www.fedev.cn/PostCSS/using-postcss-with-bem-and-suit-methodologies.htmlAir Max 1 Ultra Moire