CSS设计模式

css
  目录

CSS设计模式

css的设计模式有OOCSS,BEM,SMACSS,ACSS这几种,接下来逐一介绍。

设计模式:OOCSS

OOCSS是比较基础的设计模式,其他设计模式或多或少都有OOCSS的影子,OO就是面向对象,也就是用面向对象的思维来写CSS。

面向对象

大家都很熟悉面向对象,我们简单看下下面这一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
void study () {}
}

class Teacher extends Person {
void study () {}
void teach () {}
}

class Student extends Person {
void study () {}
}

有一个Person类,它有一些方法,Teacher和Student都继承Person,并且进行了一些修订和扩展。那么我们可以把Person类看成是别人写代码,在不改变别人源码的情况下,新增Teacher和Student对源码进行修订和扩展。这种写法是完全可以应用到CSS上面的。我们看下面一段代码场景:

1
2
3
4
5
6
7
8
9
10
<div class="menu"></div>
<div class="menu fix"></div>
<div class="menu"></div>
.menu {
color: green;
font-size: 14px;
}
.fix {
color: red;
}

场景

设计师只想让我们改变其中一个盒子的样式,其他的不变。
这时不能轻易改变menu,因为一旦改变就会影响不需要改变的地方。那么我们可以直接利用面向对象的思想,在后面单独添加一个类进行修订和扩展。这样一看我们平时在写的CSS,不就是面向对象吗?
当然,OOCSS是有具体的原则的。那么它有哪些原则且个原则的具体是什么呢?我们来看一下:

原则一:容器与内容分离

容器与内容分离顾名思义,直接看一个代码案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="post"> 
<p class="metadata">
<a>Hello</a>
</p>
</div>
<div class="comment">
<p class="metadata">
<a>Hello</a>
</p>
</div>
// ---------code 1-----------
.post .metadata {
// css code
}
// ---------code 2-----------
.post {}
.metadata {
// css code
}

场景

两个不同容器中的内容相同
先看样式的code 1,这样显然不好,容器和内容是嵌套依赖关系,并没有做到容器与内容分离。内容的样式无法复用。样式的code 2做到了容器与内容分离,内容在不同的容器中可以复用。

原则二:结构与皮肤分离

结构可以看做是一个基础对象,而皮肤可以看做是另外一个对象,也就是对象与对象要分离。基础对象不能改,不断的分离出皮肤对象达到对基础对象的修正和扩展。

1
2
3
4
5
6
7
8
9
10
<div class="menu fix"></div>
// 基础对象
.menu {
color: green;
font-size: 14px;
}
// 皮肤
.fix {
color: red;
}

OOCSS与Vue的关系

我们每天都在写OOCSS,Vue的组件就是OOCSS。我们下面一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// -------------定义组件-----------------
<template>
<div class="menu"></div>
</template>

<script>
export default {
name: 'MateData'
}
</script>
<style>
// 基础对象
.menu {
color: green;
font-size: 14px;
}
</style>
// -----------使用组件-------------------
<template>
<mate-data class="fix1"></mate-data>
<mate-data class="fix2"></mate-data>
</template>
<style>
// 皮肤
.fix1 {
color: red;
}
.fix2 {
font-size: 20px;
}
</style>

设计模式:BEM

BEM是什么

BEM即块(Block)、元素(Element)、修饰符(Modifier) 是由Yandex(俄罗斯最著名的互联网企业)的开发团队提出的前端开发理论。BEM通过Block、Element、Modifier来描述页面(关键就是为了解决多人协作的命名问题)。
Block是页面中独立存在的区块,可以在不同场合下使用。每个页面都可以看做是多个Block组成。
Element是构成Block的元素,只有在对应Block内部才具有意义,是依赖于Block的存在。
Modifier是描述Block或Element的属性或状态。同一Block或Element可以有多个Modifier,Modifier不可以单独存在。
在命名时,Block作为起始开头,不同 Block 和 Element 用 _ 两个底线区隔开来,不同的 Modifier 则用 – 区隔。
img

进阶版的OOCSS

BEM就是进阶版的OOCSS,我们看下图所示:
img

场景

页面两个tab栏,整体布局相似,只有细节部分不同
那么使用BEM写样式时,就会定义一个块menu,下面包含元素menu_tab,完成整体布局,细微部分使用修饰符menu_tab-style1进行微调。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- BEM -->
<div class="menu menu-style1">
<div class="menu_tab menu_tab-style1"></div>
<div class="menu_tab menu_tab-style1"></div>
<div class="menu_tab menu_tab-style1"></div>
<div class="menu_tab menu_tab-style1"></div>
</div>

<div class="menu">
<div class="menu_tab menu_tab-style2"></div>
<div class="menu_tab menu_tab-style2"></div>
<div class="menu_tab menu_tab-style2"></div>
<div class="menu_tab menu_tab-style2"></div>
</div>

通过上面代码可以看出BEM就是OOCSS。
对BEM感兴趣的话,可以访问BEM的官网

设计模式:SMACSS

SMACSS is a way to examine your design process and as a way to fit those rigid frameworks into a flexible thought process. (SMACSS通过一个灵活的思维过程来检查你的设计过程和方式是否符合你的架构,更像一种规范~)

核心思想:分类

  • SMACSS的核心就是分类,它主要要求分为五大类分别是:Base、Layout、Modules、State、Theme
  • Base是对浏览器默认样式的重置,常见的normalize.css就属于此。这里样式只会对标签元素本身做设定,不会出现任何 class 或id,但是可以有属性选择器或是伪类.
  • Layout对页面布局的一些功能,属于较高的一层,它可以作为层级较低的Module Rules元素的容器。左右分栏、栅格系统等都属于布局规范。SMACSS还约定命名使用l-/layout-前缀来标识布局的class。
  • Modules公共复用的小模块,模块是SMACSS最基本的思想,同时也是大部分CSS理论的基本,将样式模块化就能达到复用和可维护的目的,但是SMACSS提出了更具体的模块化方案。SMACSS中的模块具有自己的一个命名,隶属于模块下的类皆以该模块为前缀,例如:.menu .menu-title等。
  • State对不同的展示效果,例如显示、隐藏,与BEM抽取修饰类的方式的不同,SMACSS是抽取更高级别的样式类,得到更强的复用性,命名全都以is-前缀,如:is-hidden。
  • Theme对不同主题皮肤的维护,可以修改前面4个类别的样式,且应和前面4个类别分离开来(便于切换,也就是“换肤”)。命名规范需要添加theme-前缀。

最小适配深度原则

1
2
3
4
/* depth 1 */
.sidebar ul h3 {}
/* depth 2 */
.sub-title {}

两段css的区别在于html和css的耦合度(这一点上和OOCSS的分离容器和内容的原则不谋而合)。可以想到,由于上面的样式规则使用了继承选择符,因此对于html的结构实际是有一定依赖的。如果html发生重构,就有可能不再具有这些样式。对应的,下面的样式规则只有一个选择符,因此不依赖于特定html结构,只要为元素添加class,就可以获得对应样式。
当然,继承选择符是有用的,它可以减少因相同命名引发的样式冲突(常发生于多人协作开发)。但是,我们不应过度使用,在不造成样式冲突的允许范围之内,尽可能使用短的、不限定html结构的选择符。这就是SMACSS的最小化适配深度的意义。
在项目中使用SMACSS时,每一个分类都是一个目录,但是在Vue中,Layout和Modules不需要单独维护目录,因为我们写的布局组件和模块组件就相当于这两个分类了。
如果想要对SMACSS更详细的了解可以访问SMACSS

设计模式:ITCSS

这是由csswizardry提倡的一个 CSS 设计方法论,它可以让CSS更好的管理和维护。
使用ITCSS主要可以帮助我们以下几点:

  • Manages source order.(管理 CSS 代码的书写顺序)
  • Filters explicitness.(过滤器的明确性,是说分层来明确每层 CSS 的作用)
  • Tames the cascade.(控制好 CSS 的权重)
  • Sanitises inheritance.(安全地使用继承)

核心思想:分层

TCSS的核心是分层,主要分为七层, 与SMACSS的分类相比更加精细,层次分的更细主要有:

Layer 作用
Settings 项目使用的全局变量
Tools mixin,function
Generic 最基本的设定 normalize.css,reset
Base type selector
Objects 不经过装饰 (Cosmetic-free) 的设计模式,相当于SMACSS的Layout
Components UI 组件
Trumps helper 唯一可以使用 !important 的地方

下面就是ITCSS的架构模型:
img
从这个模型可以看出来,越往下就越具体,越局限于使用在某个具体的东西。另外它的下一层永远继承上一层的所有样式。

各个分层例子

Settings

全局变量,比如颜色,字体大小等等

1
2
$yellow: #FAAF00;
$yellow-bright: #FAF7F0;

Tools

mixin,function 等等

1
2
3
@mixin sample-mixin () {
...
}

到 Tools 为止,不会生成具体的 css

Generic

reset,normalize 等等

1
2
3
4
5
*,
*::before,
*::after {
box-sizing: border-box;
}

Base

type selector 比如 link, p 等等,只有这一层才使用 type selector

1
2
3
4
p {
margin: 0
line-height: 1.5;
}

Objects

Cosmetic-free,不使用比如 color、border-color、background-color 之类的
使用这个 CSS 你在浏览器上面只可以看一片空白
主要用来做画面的 layout

1
2
3
4
.o-container {
box-sizing: border-box;
margin: 0 auto;
}

Components

UI 组件
到这个部分,根据UI分析具体有哪些组件需要实现,可以分给多个人来同时实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# button组件
.c-btn {
display: flex;
justify-content: center;
align-items: center;
...
&--primary {
background-color: #ff5959;
color: #fff;
}
&--large {
font-size: 16px;
padding: 16px 14px;
...
}
}

HTML 类似这样

1
2
<a class="c-btn c-btn--primary" href="#">sample</a>
<a class="c-btn c-btn--primary c-btn--large" href="#">sample</a>

Trumps

放各种 helper
最主要的作用是用在不适合或者不容易放在 Component 的时候
比如 margin,很可能不应该放 Component,这时候可以用 Trumps 来微调
这样可以防止你的 Component 变得非常大
只有这一层才可以使用! important,以此来避免多个! important 的混乱局面

1
2
3
4
5
6
7
8
.u-color {
&--white {
color: $white !important;
}
}
.u-hidden {
display: hidden !important;
}

在使用时,每个分层都维护为一个文件夹。在Vue中使用时,Objects和Components相当于我们的组件,不需要单独维护。
另外值得注意的是,无论是SMACSS的分类还是ITCSS的分层,都是一种思想,我们可以根据实际项目来动态的添加或者删除某一个分类或者分层,不能生搬硬套。

设计模式:ACSS

ACSS使用了紧密的类名库。 这些类名通常被缩写,并与它们影响的内容分开。 在ACSS系统中,我们可以知道类名的作用; 但是类名称与内容类型之间没有关系,即每一个样式对应一个类,也称原子类CSS。

1
2
3
4
5
6
7
8
9
10
11
12
.float-left {
float: left;
}
.float-right {
float: right;
}
.z-0 {
z-index: 0;
}
.z-auto {
z-index: auto;
}

从上面的代码中,可以看到ACSS有极强的复用性,维护成本低,但是破坏了css命名的语义化。最终很可能代码会成为下面这样。但是存在即合理,ACSS也有其作用,继续往下看。

1
2
3
4
5
6
7
8
<div class="grid grid-cols-3 gap-2 place-content-center h-48 ...">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>

tailwindcss是一个典型的ACSS框架。

混合使用CSS设计模式

在进行一个项目的设计时,我们可以针对多种CSS设计模式进行选型,结合不同设计模式的优点和缺点,设计一个完整银杏的CSS架构。
假如我们选择ITCSS、BEM、ACSS进行组合,设计一个CSS架构。
在我们设计CSS架构时,我们首先想到的一定是SMACSS和ITCSS,因为它们两个对CSS进行了分类分层的划分。

SMACSS ITCSS
Base Setting
Layout Tools
Modules Generic
State Base
Theme Objects
Components
Trumps

根据上表我们可以看出ITCSS分层更加精细,所以我们选择ITCSS,接着我们继续看ITCSS的Objects和Components层,它就相当于OOCSS且相当于开发Vue组件,所以我们在开发组件时使用选择OOCSS的进阶版BEM。我们知道如果一个项目全部使用ACSS的缺点跟明显,那么我们选择ACSS的原因是因为项目中可能会存在向字体大小的这种的样式,所以我们可以把这一类样式维护在ACSS目录中。Generic和Base类似所以只保留Base即可,我假设Trumps用不到,所以也去掉这一层。所以我们的架构现在就是下面这样。

ITCSS+BEM+ACSS
Setting
Tools
Base
Objects
Components
ACSS

目录结构就是:

1
2
3
4
5
6
-|comments
-|styles
--|acss
--|base
--|settings
--|tools

总结

CSS设计模式听起来很深奥,但是大家在日常开发时,已经不知不觉的在使用,只不过是没有概念而已。在开发中熟练的使用CSS设计模式,可以使代码结构思路清晰,易于阅读,维护,如果想真正的在项目中写好CSS,CSS设计模式是必不可少的,也是我们开发者应该掌握的。