# CSS 命名那些事儿
OOCSS
SMACSS
METACSS
BEMCSS
TIP
TODO 待重构
# OOCSS 模式
面向对象的 CSS
OOCSS(Object Oriented CSS)
OOCSS 的创始人 Nicole Sllivan 提了两个主要原则
结构与样式分离:
你应该在对象中定义结构和位置,而对于样式特性应该使用类名分离出来,比如说 background 或 border。这样一来你就不需要去覆盖一些特征性样式
容器与内容分离:
不要在你的 HTML 结构中插入样式。换句话说,你的样式中尽量不要使用 html 标签或者 id 标识符。相反,应该定义一些类名
来定义样式,而且选择器的嵌套的层级应该尽量的少。
/* 不好的方式 */
.box-1 {
border: 1px solid #ccc;
width: 200px;
height: 200px;
border-radius: 10px;
}
.box-2 {
border: 1px solid #ccc;
width: 120px;
height: 120px;
border-radius: 10px;
}
/* 好的方式 */
.box-1 {
width: 200px;
height: 200px;
}
.box-2 {
width: 120px;
height: 120px;
}
.box-border {
border: 1px solid #ccc;
border-radius: 10px;
}
一句话解释: 使用class
来编写样式,每个class
都有其各自的用途。有点类似原子类 CSS 的思想。
# SMACSS 模式
可伸缩和模块化
TIP
TODO 待补充
SMACSS (Scalable & Modular Architecture CSS) 像 OOCSS 一样以减少重复样式为基础,使用一套五个层次来划分 CSS 给项目带来更结构化的方法
- Base (基本)
- Layout (布局)
Module (模块)- State (状态)
Theme (皮肤)
如果全部用上可能会显得有点繁琐,一般来说只用上 Base Layout State 就够了,如果不够用再进一步拓展。
最佳实践:Bootstarp (opens new window)
# METACSS 模式
METACSS 一些写在全局的通用方法,是 SMACSS 中通用方法思想的分支。又叫做原子类 CSS。
一般以 css 属性、Emmet css 缩写或功能来命名,通常以一个 css 属性为一个单位
表示属性的:每个属性单独成列
.display-flex {
display: flex;
}
表示功能的: 一组属性形成一个特定功能
.text-ellipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
最佳实践:Tailwind CSS (opens new window)
# BEM 模式
BEM 可能是目前最流行的 CSS 命名方式,很多主流的组件库都是使用这种命名方式,因此我们的重点放在它身上。
BEM 分别代表着:Block(块)、Element(元素/子块/组成部分)、Modifier(修饰符),是一种组件化的 CSS 命名方法和规范,由俄罗斯 Yandex 团队所提出。
# 特点
组件化/模块化的开发思路,书写方式解耦化,不会造成命名空间的污染,如:.xxx ul li 写法带来的潜在嵌套风险。
命名方式化扁平,避免样式层级过多而导致的解析效率降低,渲染开销变大。
组件结构独立化,减少样式冲突,可以将已开完成的组件快速应用到新项目中。有着较好的维护性、易读性、灵活性。规则
BEM 的命名模式在社区中有着不同方式,以下为 Yandex 团队所提出的命名规则为(本文中的代码使用该规则):
.[Block 块]__[Element 元素]_[Modifier 修饰符]
不同的命名模式,区别在于 BEM 之间的连接符号不同,依个人而定:
.[Block 块]__[Element 元素]_[Modifier 修饰符]
任何一种规范,都是基于实际需求而定,便于团队开发和维护扩展,每个规范都是经过合理评估后所得出的一种“思路”和“建议”。
# Block
Block 是一个独立的实体,即通常所说的模块或组件。例:header、menu、search
规则
块名需能清晰的表达出,其用途、功能或意义,具有唯一性。
块名称之间用-连接。每个块名前应增加一个前缀, 这前缀在 CSS 中有命名空间(如:m-、u-、分别代表:mod 模块、ui 元件)。 每个块在逻辑上和功能上都相互独立。
由于块是独立的,可以在应用开发中进行复用,从而降低代码重复并提高开发效率。
块可以放置在页面上的任何位置,也可以互相嵌套。同类型的块,在显示上可能会有一定的差异,所以不要定义过多的外观显示样式,主要负责结构的呈现。这样就能确保块在不同地方复用和嵌套时,增加其扩展性。
综上所述,最终我们可以把 BEM 规则最终定义成:
.[命名空间]-[组件名/块]__[元素名/元素]--[修饰符]
情景
需要构建一个 search 组件。
写法
.m-search {
}
结构
如果打算开发一套框架,可以使用具有代表性的缩写,用来表示命名空间:
Element UI(el-)、Ant Design(ant-)、iView(ivu-)。
# Element
是块中的组成部分,对应块中的子元素/子节点。例:header title、menu item、list item
规则
元素名需能简单的描述出,其结构、布局或意义,并且在语义上与块相关联。块与元素之间用__连接。不能与块分开单独使用。
块的内部元素,都被认为是块的子元素。一个块中元素的类名必须用父级块的名称作为前缀,因此不能写成:blockelem1elem2。
情景
search 组件中包含 input 和 button,是列表中的一个子元素。
写法
.m-search {
}
.m-search__input {
}
.m-search__button {
}
结构
<form class="m-search">
<input class="m-search__input" />
<button class="m-search__button">Search</button>
</form>
原则上书写时不会出现两层以上的嵌套,所有样式都为平级,嵌套只出现在比如 .m-block_active
,状态激活时的情况。
# Modifier
定义块和元素的外观、状态或类型。
例:color、disabled、size
规则 修饰符需能直观易懂表达出,其外观、状态或行为。修饰符用_连接块与元素。修饰符不能单独使用。在必要时可进行扩展,书写成:block__elem_modifier_modifier,第一个 modifier 表示其命名空间。情景 假定 search 组件有多种外观,我们选择其中一种。并且在用户未输入内容时,button 显示为禁用样式。
写法
.m-search {
}
.m-search_dark {
}
.m-search__input {
}
.m-search__button {
}
.m-search__button_disabled {
}
结构
<!-- dark 表明 search 组件的外观 -->
<form class="m-search m-search-form_dark">
<input class="m-search__input" />
<!-- disabled 表明 search__button 的状态 -->
<button class="m-search__button m-search__button_disabled">Search</button>
</form>
很多人觉得 BEM 写法难看,刚刚接触可能是会觉得有点奇怪,但所有东西都有一个适应过程。如果仅仅为了好看,规避其优点,我认为得不偿失。个人建议可以尝试使用 BEM 规范来书写代码。
BEM 命名会使得 Class 类名变长,但经过 GZIP 等压缩后,文件的体积其实并无太大影响。
就和早年提出 CSS 语义化 一样,不要为了语义而去语义,语义化本身的作用就是帮助大家更好的识别代码,所有的规范都是基于项目的发展和团队的协作,团队可以根据成员的意愿选择最合适的方式。
# 我的 CSS 模式
- 全局样式遵循类名控制一个属性或一组功能的样式,遵循 BEM 命名
- 组件内的样式,使用 Less/Sass 合理嵌套,遵循 BEM 命名
- 元素的状态,使用
.active
,.current
,.disable
,嵌套在 BEM 之后
/* 全局 */
.display-flex {
display: flex;
}
.flex-1 {
flex: 1 1 0;
}
/* 组件 */
.el-input {
height: 28px;
}
.el-input_inner {
color: #ccc;
}
/* 状态 */
.el-input_inner.active {
color: #777;
}
# 参考资料
博客 CSS 的模組化方法 (opens new window)
Sass 使用 Sass 来写 OOCSS (opens new window)
知乎 前端使用原子类的优劣? (opens new window)
知乎 如何看待 CSS 中 BEM 的命名方式? (opens new window)
掘金 如何写出一套可维护的 CSS 库? (opens new window)