从模块化CSS到面向对象的CSS

最近做了一个项目,尝试了一些以前未曾上尝试过的方法——基于模块设计整个网站的内容。做到后期才发觉如果初期的时候设计的更好一些,能做到面向对象的CSS来设计,那整体开发效率以及代码复用方面会做比现在好。现在的代码还是有点不干净的地方,有些地方有点乱。这可能是这行当的“完美主义”情结吧 :) 希望下次可以做的更好。

1.模块化问题

首先,什么是模块化?我这里指的模块化并非仅仅指CSS模块化,还指的是HTML的模块化。那为什么要模块化?最主要特点是使各个部分相对独立,同时增加代码复用,最终目的是为了降低开发成本提升开发效率。

举个例子,当拿到一个项目的时候,扫一遍所有设计,就可以发现:
哦,一共有10种侧边栏设计,设计师就是把这10种侧边栏翻来覆去放在不同的页面中。那就很简单了,只要完成了这10种侧边栏,那么就算100个1000个页面也都是一样的,只要把HTML复制,粘贴就可以了。最大化的实现的代码复用。

鬼哥写过一篇文章来阐述这个问题:
http://www.cssforest.org/blog/index.php?id=134

2.面向对象的CSS

当进一步观察,很容易发现,有侧边栏都是有一个标题,再下面是是主面板,再有一类侧边栏是标签页的,下面展示的是新闻之类的内容。这个时候就完全可以抽象出两个类,一个是带标题的侧边栏(title_box),另一个是带标签的侧边栏(tab_box)。这样的好处是可以进一步抽象出代码,提高复用率。增加开发速度。
比如前者的HTML可以是这样的:


可能的CSS是:

.title_box {margin-bottom:10px;border:1px solid #aaa;}
.title_box h3{border-bottom:1px solid #aaa;}
.title_box .act{float:right}
.title_box .panel{margin:0 10px;}

为了保证代码的简洁易读,一些小的padding或者背景修饰我就不写了。
其中act是action的意思,这里偷学来的XD

是的,以上的HTML以及CSS代码就共同构建了一个基本的侧边栏类
他所具有的特诊是有边框,下面空开10px,右上角还有个“更多”

有同学可能会问,我为何要把act类放在那个span上而不是a上?为何要把panel放在一个div上?放在ul上不是可以减少代码么?这里我之所以这样写,是为了最少代码与开发成本之间平衡的考虑。所有成本中,人力成本是最高的,服务器有钱都可以加,但是代码得更快速的写出来才是要紧的。这么写的话可以最大保证通用性。同时,另一个理论用于支持这种写法的是:只有无语义的div和span适合拿来做布局,而有语义的元素不应当参与到布局中。当然,我这里只是一种权衡。

这么写是很灵活的一种写法。如果不需要“更多”,直接删除即可,如果不需要标题,直接删除即可。进一步来看,这段HTML可以应用到整个网站各个地方而不仅限于侧边栏。只要是形式相同的地方都可以用。

3.进一步实现继承和扩展

这里带来的一个问题是如何控制宽度。侧边栏和正文部分宽度不同,有些地方需求又各异,如何保证宽度?跳出思维局限来看,是不是所有某一宽度的div都同属一类?是的,他们是同一类的,所以只要定好了宽度,继承就可以了!

如果应用了栅格化布局,就能很容易理解这一点,那么上面的代码就应该是这样的


前面的g5就代表了其占了5个栅格!这个模块首先是占了5个格子的宽度,然后他是一个带标题的模块。进一步看,如果这个格子在不同的地方背景色不同,那可以继承了前面两个类的基础上,再加上一个 bg_blue于是这就成了:


没错,他就是一个占5个格子的带标题的模块,背景色是蓝色的。
好,可能他在某个地方是左浮动的,需要加上float:left,但不是所有的title_box都需要做浮动,那自然加上一个left类即可:


所以,挑选一个合适的基于栅格的框架很有意义,如YUI,BluePrint,或者OOCSS。
可以参考一下OOCSS,我很欣赏其中的思想
前端观察对其的介绍:http://www.qianduan.net/object-oriented-css.html

针对面向对象CSS的一个很好的PPT
http://www.slideshare.net/stubbornella/object-oriented-css

面向对象的CSS框架(OOCSS)的项目地址
http://wiki.github.com/stubbornella/oocss

我除了不喜欢其中到处都是为了圆角而设计的CSS之外,其他都很喜欢。

另外,写的时候,我搜索了一下,才发现这种思想08年初嗷嗷就已经有啦,我Out啦:
http://www.aoao.org.cn/blog/2008/01/oo-style/

4.这不是银弹

要坚信,在解决一个问题的同时必然会带来其他问题。

优先级问题
其中一个问题是优先级问题,由于模块细分,优先级很容易弄得焦头烂额。我的经验是每个类都负责一个部分——布局归布局,色彩背景归色彩背景。只有把各个类细分出来,才能避免样式的冲突。最后,id也是必不可少的。也需要important。比如上面,我就会定义一个noborder类,表示这个容器一定没有边框。用于个别地方是一个title_box但没有边框的情况。这个类里我就设置了

.noborder{border:0 !important;}

这样就能保证在任何时候,都不会有边框了。

同时,我对于文件规划,通常是一个页面只引入这个页面的css,比如首页的index.css
在这个css里再用import依次导入用于
重置的reset.css(重置标签类,其实一个标签本身不就是一个类么?div类,span类,本身就具有block或者inline属性,我们只是在其上面在扩展而已)
global.css(用于设置header和footer以及中间主体部分的布局,因为这两个部分一般整站是不会变的)
layout.css(用于设置栅格,构建内容以及侧边栏的宽度等。)
module.css(用于建立如title_box这种中等模块)
submod.css(这里放一些细节的模块,比如某一个新闻列表的完整细节样式设定)
如果模块较多,还可能分成若干的子块,侧边栏放侧边栏,中间的放中间。因为我相信绝大多数情况下侧边栏将永远是侧边栏,而不会跑到中间去。
如果有必要,也会建立form.css,专门设置表单元素,定义表单元素的外观。我会在里边设置一些 blue_btn,red_btn之类的class来定义不同颜色的button.
而后,在这个首页的index.css里写特定的样式。
这种规划的方法在很多文章中都能见到影子,如今再回头看,又有了更深一层的体会。

另外,import有潜在的性能问题。见这里
只是我实在抵御不住import带来的便利性,在html里加入一堆的link是多么痛苦的事情。而且大多数时候对性能的要求没那么高,为此我宁可节约一点人力成本。这是种取舍,因网站需求而异。但总体顺序还是不变的。
而解决这个性能问题的方法是用 条件CSS,可以参考这里
这里
id与class
命名的时候到底是以id为主,还是class为主,想必各位同学看到这里也该明白在下的观点了——以class为主,id为辅。因为同一段HTML可能用到页面的任何地方,而id在同页面里是不能重复的。而且,为了体现继承扩展的关系,应当使用class了。
那么id还用不用?当然要用,且不论id在写JS时候的速度,实际在写页面的时候,肯定会某一个模块做“个性化”定义,这就需要使用id了。个性化的东西写在id里,这样就不用担心干扰其他模块了,最大避免触一发动全身的窘境。通用与独立同在。

聪明的同学一定想到了什么,对,是不是记得曾经看到过启蒙读物上,一再强调,千万不要把一个类明明为left,因为指不准哪天他就不在左边了。不要定义为blue,因为哪天就改成红色了?是的,那个说没错,但我这里也没错。我们说的是两个东西。

修改CSS还是修改HTML

曾经看到的那些,都是建立在一旦HTML写完,需求变的时候不许改HTML,只许改CSS的基础上的。但实际上,又多少机会能让你只改CSS而不改动HTML的呢?另外,既然有一套完善的CSS了,为何不直接简单修改一下HTML呢?如果背景变变成了红色,我只需要修改一下class,从bg_blue改成bg_red就可以了,何乐而不为?由此看来,class取名成left和right并非很糟糕的决定,只要合理利用,完全可以应付多样的需求。

开发成本VS维护成本
这样做之后带来的问题就是开发成本与维护成本的矛盾。这样开发确实很快速。但问题是以前小改动起来是不需要动HTML的,而现在改一次就得改HTML,而这方面一旦页面做好后就可能归其他部门那边管了,修改测试上传都得惊动其他部门,这方面如何协调是一个问题。
换句话说:面向对象CSS保持CSS框架不变而修改HTML,而原先那种是保持HTML不变而修改CSS
这方面我没有太多经验,欢迎大家探讨。
Twinsen有过类似的探讨( http://www.twinsenliang.net/skill/20081129.html ) 他就很反对这么来。
其中的大部分观点都赞同,只是最近我也在反思:
到底多大机会能让我碰上“left_box”和“right_box”的互换。全站的blue_button换成红色呢?
如果只是一两个页面的话,修改CSS的成本更低,还是保持CSS不动修改HTML的成本更低?

增加学习成本
当公司招募新人的时候,不得不对其进行培训,以使其学会使用这套自建的框架体系。但带来的好处是使整个网站风格统一,给进一步维护带来方便。所以我认为增加的这点学习成本是值得的。
同时,如果你都已经跳槽到别处了,没留下好的文档,人家学不会也没关系。直接抛开这套体系也能过日子。因为本身这套体系是从reset开始对各个标签初始化对象的,而后逐渐增加class以扩展对应属性。
也就是说,新人也可以抛开这套东西,直接用自己的写法来写。当然,这好不好已经不在今天的讨论范围了。这里只是为了说:这个框架也可以让新手不管这些约束直接写自己的。

class过长导致文件过大
这可以通过压缩来解决。用正则找出css和html中所有的class,以出现频率排序,然后分别从abcdeft…开始,先单字母,后双字母 a一直到zz应该有26*27种组合,足够一般网站应用了。google应该也是这样做的。抱歉这段时间比较慢,代码就没写了。

副作用:js必须用id而不是class了,否则压缩后会找不到对应的class
解决思路:约定JS中保存class的字符串变量为 foo_cls=”bar”,那么只要用正则找到对应_cls结尾的变量进行对应替换即可。
这本身是面向对象的CSS,而进一步压缩这里仅提供一种思路。

总结

结构和样式分离是好事,我这里提出的观点反而有点逆潮流,试图通过HTML中的class属性来控制表现。我认为这是一种理性回归,至少我们不用一个ID下面写一堆的针对他的样式,冗余,容易出错,效率还高。同时这种方式与直接在元素上写style也是有本质区别的,模块化,面向对象的思想体现在其中,合理控制每一个类的颗粒度,或者说控制范围。
我这种介于结构—样式的完全分离与结构—样式完全整合之间的一种状态,试图寻找一种平衡,提升开发效率并降低维护成本。

谨以此文做以探讨。

扩展阅读

重构之美-跨越Web标准,触碰语义网[分离:通用也许是个美丽陷阱] ——另一个反对的声音
面向对象的CSS--OOCSS 国人写的通过JS增加CSS面向对象功能

《从模块化CSS到面向对象的CSS》有22个想法

  1. 分析的很理性,我看前一段的时候想到的一些问题,都在后面给了解答。很赞。
    不过感觉使用import会有一定的性能问题,实际的经验没有了,记得 有篇文章是说import性能问题的。

  2. 你把修改CSS和修改HTML对比,可是我觉得,只改css不改html的情况确实会有,只改html不改css的情况很少有吧……一般要改html了多半css也要改的……

    1. 比如说,当你使用了YUI框架,自然不会再动YUI框架里的代码。当然,后来添加的CSS自然会变动。
      其实,只改CSS不改HTML的情况真的多么?
      其实你说的很好,改HTML结构大多都要改CSS,但结构不变的时候要求改变外观,可以只改class,也可以只改CSS。具体改哪个,自己看着办。甚至完全无视CSS框架,直接自己手写一套都可以~

  3. 看完以后还是很有同感的。在实际操作过程中,原先使用的css的继承方法来提高编写效率和维护,后来发觉在后期维护过程中继承还是会对那些并没有定义过样式的标签产生影响,必须写的相当严密。

    css的组合确实是一个不错的方法,不会影响那些无定义标签。但也有一个问题就是在后期维护时,我们需要去修改页面,还有样式,这样做有没有违背页面和样式分离的本意呢?在后期维护上是否显得有些不便?

    1. 后期维护对于我来说现在还只能是YY,我不能不负责任的推广而无视潜在的问题。所以,是不是值得使用,仁者见仁智者见智。而至于分离的思想是不是合适,可以参考我最后总结的那一段

  4. 另一个理论用于支持这种写法的是:只有无语义的div和span适合拿来做布局,而有语义的元素不应当参与到布局中。

    这样理解就太片面了,布局是样式思维,语义是结构思维。怎么能混到一起?
    你的结构合理后,采用样式去修饰到最佳用户体验的程度。

    1. 适当增加代码冗余有助于快速开发以及代码的模块化。就我目前的认知水平觉得这样比较好。之前一直追求最少的标签,完全用css控制布局以及表现,感觉样式与结构之间耦合的太紧,不容易代码复用。而现在这样感觉好很多。一家之言,欢迎探讨~

  5. 关于修改CSS 还是修改HTML的思考,是把所有图片都写进css里好,还是只把一部分作为背景的图片写到CSS里,不是背景的图片直接写到HTML里好呢?我个人觉得后者较好,这样图片下载会更快点吧,看来你的文章真是受益匪浅啊,才感觉自己不只是out了一点点。

  6. 很不错的文章,总结蛮全面的,特别是第4部分“这不是银弹”,写到了实际工作中的一些特点,像“修改CSS还是修改HTML”的问题就很有针对性。能有这样的思考很不错,不过总体感觉有些“点到为止”,不知道是不是写成文章的原因。

    另外对于文章中的“继承”,貌似有点理解偏了,类间是没有继承关系的。

  7. 我觉得OOCSS和栅格化布局的确是让HTML和CSS的耦合更紧密了,因为他们要在HTML的class中添加和样式有关的内容,我不知道这是不是一个好的方向。在未来Web越来越语义化的过程中,一般的Web Agent(现在主要也就是搜索引擎)仅仅关心Web文档的内容,而不会去关心样式。所以我觉得样式的改动最好还是在CSS中进行。理想中的Web提倡内容和样式分离,但这个想法目前还不太实际…感觉现在这套HTML+CSS的体系是不可能把内容和样式完全分离开来的。我个人认为目前的这些概念都只是当前体系下的折中方法,虽然有不少好处,但也并不太符合Web发展的趋势。这个话题注定是会引起争论的吧~

  8. 这个想法很早就有接触到了,但目前还是单个控制,一直习惯不了多个,看来是时候该改了。

  9. 分析的很透彻,中间看到许多相似的用法,特别是noborder这个深有体会!不过这样就想你说的因为耦合度高,所有优先级就比较重要了

发表评论