CSS架构(I)—— CSS该怎么写

CSS应该怎么写?当被问到这一个问题的时候,我懵了。之前在个人项目中写CSS都是随心而写,最后显示效果也还可以,所以一直没注意到这个问题。回想到之前在修改样式的时候遇见了修改文章的样式却造成了全局布局的更改,原因是文章的容器类与一个全局布局相关的容器类意外重名了。在小型项目中尚且如此,如果在大型项目中随心而写,后果那就可想而知了。emmmmmm。CSS该怎么写,确实是一个值得思考的问题。

好的CSS应该是怎么样的

在讨论怎么写CSS好之前,首先要解决的问题是——好的CSS是什么?好看?好用?或者是…

在谷歌工程师Philip Walton的这篇 CSS Architecture中 提到了好的CSS应该是可预见的、可复用的、可维护的与可扩展的。

可预见的

设计良好的CSS规则可以让开发者可以预见应用规则之后元素的样式。比如一个.big-red-btn规定了一个大红按钮。那么我们在某个元素中应用class="big-red-btn"时,这个元素就应该是一个大红按钮。

可复用的

CSS规则应该足够抽象与解耦。这样,同一条CSS规则就可以在整个工程的不同地方使用。可复用性最 直观的好处是CSS代码的体积的减少。而且在进一步开发中或项目进行重大重构时,如果项目使用了可重用的规则,我们更改代码的工作量就可以大大减少。

可维护的

我们希望开发新功能时不需要去重构原来的代码。增加一个组件不会影响另外一个相关的组件。

可扩展的

当新的开发者接手项目时,不需要太难的学习曲线就可以上手开发。如果在为新的组件编写CSS规则时需要了解过去全部的CSS规则,这样的CSS代码是不可扩展的。

常见的Bad Practices

文章列出了一些常见的Bad practices,例如:根据父元素改变组件、过于复杂的选择器、过于一般的类名以及在单条规则里做太多事情。这些Bad practice我是全中了,ORZ。

根据父元素修改组件

在网站中可能有些在不同区域中外观有细微差别的组件,比如一个小物件在侧边栏中以及主页中表现不一样。我们很容易写出像下面的CSS规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.widget {
background: yellow;
border: 1px solid black;
color: black;
width: 50%;
}

#sidebar .widget {
width: 200px;
}

body.homepage .widget {
background: white;
}

这个看起来无害的.widget可以数出它的三宗罪。第一、这个.widget不符合可预见性的要求。在同样的HTML标签<div class="widget"></div>放在不同的地方它的表现就不一样。第二、.widget规则不好复用,当我就想在侧边栏上用普通外观的.widget时该怎么办呢?只能搞一个新的copy-and-paste的规则。第三、.widget规则不好维护,当.widget被重新设计的时候,我可能需要到几个不同的地方把样式找出来修改。

过于复杂的选择器

在给列表,特别是嵌套列表写样式的时候,很容易写出下面一串CSS规则:

1
#main-nav ul li ul li div { }

上面的规则问题在哪里呢?假如导航栏里面的HTML结构一辈子不变,这样的写法还可以商榷。一旦将来改变,上面的规则将会失效。这个问题总结起来就是过于复杂的选择器会造成过深的耦合。太深的耦合在程序世界里不是什么好事情。另外,这样的规则也无法复用,一个页面只能有一个#main-nav,后面的元素如果不在#main-nav里面也无法匹配到规则。

过于一般化的类名

在写可复用组件的时候,很容易给组件取一些很一般的名字像titlecontent。在大型项目中,可能其他人也会给他们的组件取相同的名字。由于CSS没有作用域的概念,这样就容易出现命名冲突。当写出一个规则但是完全不按写的规则来的时候,我们就会很疑惑,然后debug发现自己写的规则被其他人写的同名规则覆盖……WTF!上面的例子也就是说,太一般的类名是没有可预见性的。

单条规则中做了太多事情

有的时候,我们会在一条规则中把所有该指定的东西都指定完——如元素的位置、背景、字体设置等等,像:

1
2
3
4
5
6
7
8
.widget {
position: absolute;
top: 20px;
left: 20px;
background-color: red;
font-size: 1.5em;
text-transform: uppercase;
}

.widget一条规则设置了组件的位置、背景、字体。这一条看起来也是人畜无害的规则。 但是仔细一想的话,这条规则似乎无法复用。 如果我想把.widget用在绝对定位元素的右下角怎么办?当我们把规则写的越细, 那么这条规则适用的范围就越小。用在CSS世界中就是,规则的具体程度与可复用性是矛盾的。

从上面这些bad practice反面去理解,我们可以摸到一些如何写CSS的门路:

  1. 谨慎使用后代选择器:后代选择器似乎像是万恶之源一样,后代选择器会在规则之间创造一定的耦合,影响规则的可预见性、可重用性、可维护性等等。
  2. 合理拆分规则:太具体的规则不好复用。但是反之规则太笼统的话,就会需要很多规则才能做一件事情。所以,怎么拆分规则是一个trade-off问题。
  3. 类命名空间机制:CSS是没有命名空间的概念的,要减少命名冲突的可能性,势必要采取严格的命名规范隔离开不同的规则。

当然,上面的想法还是过于粗略。社区在如何写CSS方面有了一些探索,其中最有名有 OOCSS(Object Oriented CSS)、 SMACSS(Scalable and Modular Architecture for CSS)与BEM(Block, Element, Modifier),如果有兴趣的话可以浏览一下这些规范。