CSS的命名——命名空间的概念
CSS的命名应该注意哪些问题昵?我们以下图为例,进行详细解析。
首先,CSS的命名推荐使用英语,不要使用汉语拼音。我们可以根据内容来选用合适的英文单词命名CSS 。比如头部用head,底部用foot,主体部分用main,导航用nav,菜单用menu等,这些不是硬性的要求,但为了方便阅读和理解,提高可维护性,我们推荐使用这样的命名方式。很明显,上图是一个关于时间的无序列表,我们可以用timelist来命名它。如代码清单4-4所示。
代码清单1 CSS命名方案一
- <style type="text/CSS">
- .timeList{xxxxxxx}
- </style>
- <ul class="timelist">
- <li>2009年08月(3)</li>
- <li>2009年07月(12)</li>
- <li>2009年06月(3)</li>
- <li>2009年05月(8)</li>
- <li>2009年04月(2)</li>
- <li>2009年03月(8)</li>
- </ul>
timelist用到了两个单词:time和list,但它们连在一起,很难一眼认出来,可读性很差。如果命名需要用到两个或两个以上单词,常见的做法有两种:骆驼命名法和划线命名法。骆驼命名法是从笫二个单词开始,将每个单词的首字母大写,例如dropMenu、subNavMenu。而划线命名法是将每个单词用中划线“-”或下划线“_”分隔,例如drop-menu、sub_nav_menu。使用骆驼命名法和划线命名法都可以清楚地将单词划分开来,提高命名的可读性。但笔者认为单独使用骆驼命名法或划线命名法并不是最好的实践方式,推荐将这两种方法组合使用。
代码清单1中每个 li 都有一条下划线,但最后一个没有,因为IE6不支持:last伪类,所以我们不能简单地使用.timeList li{border-bottom:×××}和.timeList li:last{border-bottom:none;)来实现样式。我们只能为最后一个li挂个特殊的class来实现想要的效果,如代码清单2所示。
代码清单2 CSS命名方案二
- <style type="text/CSS">
- .timeList{xxxxxxx}
- .timeList li{border-bottom:xxxx;}
- .timeList .last{border-bottom:none;}
- </style>
- <ul class="timeList">
- <li>2009年08月(3) </li>
- <li>2009年07月(12) </li>
- <li>2009年06月(3)</li>
- <li>2009年05月(8)</li>
- <li>2009年04月(2)</li>
- <li class="last">2009年03月(8)</li>
- </ul>
为最后一个li标签挂上名为last的class,可以实现我们想要的效果,但这样的命名还不够好。这涉及一个问题——滥用子选择符。很多工程师喜欢使用子选择符,但滥用子选择符容易留下冲突隐患,我们在编写代码时应该时刻保持“团队合作”的意识,将冲突隐忠降到最低,提高代码的可维护性。last是个过于简单且常用的命名,如果工程师A过于依赖子选择符,可能使CSS代码里出现大量类似于“.timeList .last”、“.namcList .last”、“.ageList .last"这样的选择符,如果多人合作,工程师B可能习惯直接使用“.last”作为选择符,从而和“.timeList .last”等选择符设置的样式层叠,产生意料之外的影响。除了“.last”,常见的容易产生冲突的命名还有“.first”、“.item”等。
为了将风险降至最低,不推荐轻易使用子选择符,可以改用如代码清单3所示的命名。
代码清单3 CSS命名方案三
- <style type="text/CSS" >
- .timeList { xxxxxxx }
- .timeList li|border-bottom:xxxx; }
- .timeListLastItem{border-bottom: none; }
- </style>
- <ul class="timeList">
- <li>2009年 08月 (3)</li>
- <li>2009年 07月 (12)</li>
- <li>2009年 06月 (3)</li>
- <li>2009年 05月 (8)</li>
- <li>2009年 04月 (2)</li>
- <li class="timeListLastItem">2009年 03月 (8)</li>
- </ul>
我们使用“.timeListLastltem”代替“.timeList .last”来作为选择符,就可以降低冲突隐患了。但这样的命名仍然不够好——将整个ul视为一个模块,在结构上“.timeListLastltem"是从属于“.timeList”的,但命名上看不出明显的从属关系。虽然CSS没有真正意义上的“封装”功能,但如果CSS命名能够表现出从属关系,就可以相对地让模块的封装性更好。如前所述,我们可以结合骆驼命名法和划线命名法来进行命名,其中骆驼命名法用于区别不同单词,划线用于表明从属关系。
“.timeListLastltem”可以改进为“.timeList-lastItem",如此一来,进一步提高了CSS命名的可读性,不仅能从命名中清楚看出各个单词,还能了解到从属关系。很明显,“.timeList-lastItem"是“.timeList"的“.lastItem”。
既然划线用于表明从属关系,那么不推荐使用诸如“.timeListLastItem"、“.time-list- first-item"这些命名方式。
(引用)在4.2节中我们讲过CSS可以分为base、common和page三层。(引用) 其中base层和common层是公共的,由于统一加载到所有页面里,一般只会由一个人来负责,不会出现冲突问题。而page层是页面级的,可能会由多人合作完成,有可能存在冲突隐患。我们在命名CSS时,首先要判断它是位于什么层的,如果模块多次反复出现,那么应该将它归为common层,不用考虑冲突问题,而如果出现的次数少,那么应该将它归为page层,需考虑如何避免冲突。
假设图中所示的模块位于page层,这个页面有多个模块,因为某种原因,图中所示的模块由工程师A制作,而另一个模块由工程师B制作。工程师B有可能为自己负责的模块取了同样的名字timeList,如代码清单4所示。
代码清单4 CSS命名冲突
- <style type-"text/CSS">
- /* made by工程师A */
- .timeList{xxxxxxx}
- .timeList li{border-bottom:xxxx;}
- .timeList-lastItem{border-bottom:none;}
- ...
- /* made by工程师B */
- .timeList{ }
- .timeList li{border-bottom:xxxx;}
- </style>
- <!-- made by工程师A -->
- <ul class="timeList">
- <li>2009年08月(3)</li>
- <li>2009年07月(12)</li>
- <li>2009年06月(3)</li>
- <li>2009年05月(8)</li>
- <li>2009年04月(2)</li>
- <li class="timeList-lastItem">2009年03月(8)</li>
- </ul>
- ...
- <!--made by工程师B-->
- <ol class="timeList">
- <li>2009-08-07</li>
- <li>2009-08-06 </li>
- <li>2009-08-05</li>
- </ol>
工程师B因为没有注意到工程师A写的代码,给自己的HTML标签挂了同名的class,在无意中和工程师A编写的代码产生了冲突。如何避免这样的冲突呢?可以通过给命名加前缀的方式解决这个问题。每个加入到团队中的工程师,需分配一个唯一的标识符,这个标识符加上划线作为自己所负责的page层CSS命名时的前缀。比如说,可以使用姓名首字母缩写作为标识符,工程师A叫做“阿当”,其姓名拼音为“a dang”,取首字母缩写,其标识符为ad,工程师B叫做“张霞’’,其姓名拼音为“zhang xia”,取首字母缩写,其标识符为ZX,重写代码清单4,如代码清单5所示。
代码清单5 CSS命名加前缀
- <style type="text/CSS">
- /* made by工程师A */
- .ad-timeList { xxxxxxx }
- .ad-timeList li{border-bottom:xxxx; }
- .ad-timeList-lastItem{border-bottom: none; }
- /* made by工程师 B */
- .zx-timeList{ }
- .zx-timeList li{ border-bottom:xxxx; }
- </style>
- <!-- made by工程师A -->
- <ul class="ad-timeList">
- <li>2009年 08月(3)</li>
- <li>2009年 07月(12)</li>
- <li>2009年 06月(3)</li>
- <li>2009年 05月(8)</li>
- <li>2009年 04月(2)</li>
- <li class="ad-timeList-lastItem">2009年03月(8)</li> </ul>
- <!-- made by工程师B -->
- <ol class="zx-timeList" >
- <li>2009-08-07</li>
- <li>2009-08-06</li>
- <li>2009-08-05</li>
- </ol>
加了唯一前缀之后,工程师A和工程师B都可以专心于自己负责的代码,不用担心冲突的问题了。
但如此一来,存在一个问题——CSS命名过长。在base层,因为CSS只包含原子类,并且是所有团队成员共享的一层,所以功能单一,不存在划线分隔和命名前缀的问题,这层的CSS命名大多非常简短。在common层,由于有了组件概念,根据组件的复杂程度,CSS命名可能会比较长,例如“infoList-firstltem-img"、“nav-item-select”等。到了page层,因为需要加前缀,命名可能会更长,例如“ad-dropMenu-lastltem-img"、“zx-subNav-item-select”。命名长可以带来很好的可读性,可以避免多人合作的冲突,但也会增加文件大小,有些工程师可能会因为这点而反对使用过长的命名。这的确是个无法兼顾的问题,但权衡两者,过长命名影响的只是纯文件,比起图片和Flash等资源,文件大小并不会很大,它带来的坏处在可接受范围内,而它带来的好处却是非常明显的。
面向对象编程里有“公共”( public)和“私有”(private)的概念,它是用于“封装”的强有力武器。通常,我们将一个类看成一个黑盒,公共属性和公共方法是这个黑盒与外界交流的接口,而所有私有属性和私有方法全部封装在黑盒内部。一个优秀的面向对象设计会控制类提供尽可能少的接口,除了必要的属性和方法会设计成公共的,其他属性和方法全部会设计成私有的。由于外界能与类交流的只有公共的部分,所以无论如何修改类的私有部分,只要保证公共部分不产生变化,就不会造成对其他类的影响,类的修改就是安全的。类的公共部分越少,类就越容易维护,所以我们应尽量将属性和方法设计成私有的。
可以将同样的思想借鉴到CSS领域。用划线作为从属关系分隔符,其实是将模块视为了类,从属于模块的元素被视为了类的属性,通过命名上的直观,得到一种“封装”。类似于“.last”这样的命名,相当于是公共变量,如果对其修改,很容易产生全局范围内的影响,所以并不推荐使用:而“.timeList-lastItem”相当于“.timeList”范围内的私有变量,“.timeList”起到了命名空间的作用,对其修改只会作用于“.timeList"组件内部,而不会产生全局的影响。从加前缀的角度看,不加前缀的base层和common层就像是公共属性,适用于全局(所有页面)范围内,而加前缀的page层就像是私有属性,只适用于特定页面。
比起代码清单6所示的命名方式,笔者更倾向于使用代码清单7所示的命名方式。
代码清单6 不加前缀的命名方式
- <div class="box">
- <div class="hd"></div>
- <div class="bd"></div>
- <div class="ft"></div>
- </div>
代码清单7 加前缀的命名方式
- <div class="box">
- <div class="box-hd"></div>
- <div class-"box-bd"></div>
- <div class="box-ft"></div>
- </div>