分类: Html/CSS | 转载请注明: 出自 海玉的博客 
本文地址: http://www.hicss.net/separation-of-powers-model-in-css-design-patterns/

市面上我们常常会看到各种各样的设计模式书籍,Java设计模式、C#设计模式、Ruby设计模式等等。在众多的语言设计模式中我唯独找不到关于CSS设计模式的资料,即使在网上找到类似内容,细细一看之下才发觉是南辕北辙。经过浩瀚文章搜索发掘下依旧一无所获之后,直接导致了我萌生一股写一篇CSS设计模式的冲动,至此写下这篇文章,其中叙述如有不当之处,也恳请各位提出意见,分享出您宝贵的经验。

在写页面之中,width, margin, padding这三个CSS属性可以说是用到频率最高的几个属性之一。但根据我的观点来看,许多人,甚至于大多数前端对于这三个属性的书写把握上乏善可陈,以至于兼容和灵活性不得兼顾,导致日后的开发维护成本直线上升,代码不断增长,覆盖重写样式,接着再修复一个又一个的Bug。这样的情况下,使用一种合理高效的CSS设计模式不失为一种明智的选择,个人称之为:width,margin,padding三权分立模式(以下简称三权分立模式)。

什么是三权分立模式:

说明这个模式之前,必定先要明白什么是盒子模型,你可以参考如下Firebug的盒模型截图:

注意:在图上表示margin的颜色为白色(实质是透明)。Margin比较特别,它不会影响盒子本身的大小,但是它会影响和盒子有关的其他内容,因此margin也是盒模型的一个重要的组成部分。

盒子本身的大小是这样计算的:

Width: width + padding-left + padding-right + border-left + border-right
Height: height + padding-top + padding-bottom + border-top + border-bottom

通过图中我们得知,Width(物理总宽度)/Height(物理总高度),是由width/height,padding,border三者之和来决定的。但简单浅显的盒模型一旦牵涉到CSS中,便会出现不小的意外。

假设,需要要一个300px宽,80px高的盒子,里面放一段文字,文字离边有10px间距。那么一般Html和CSS写法为:

HTML代码:

 <div class="box">
市面上我们常常会看到各种各样的设计模式书籍,Java设计模式、C#设计模式、Ruby设计模式等等。
</div>

CSS代码:

.box{width:280px; height:60px; padding:10px;}

来分析一下这段CSS代码,因为需要满足300px宽,且文字间隔为10px,所以Width(300px) = width(280px) + padding(10px) X 2。假设过了段时间需要改版,要求为这个Box添加1px颜色为#ccc的边框,那么我们再次找到这段CSS,开始动手修改,加个boder:1px solid #ccc;:

CSS代码:

.box{width:280px; height:60px; padding:10px; border:1px solid #ccc;}

之后测试一下,咦!似乎有点问题,哦,因为加了边框所以又多了2px像素的宽,所以要减去这2px。

CSS代码:

.box{width:278px; height:58px; padding:10px; border:1px solid #ccc;}

测试一下,问题解决了。正当欢心不已准备休息时候,设计师跑来说,根据最新要求,希望能再把文字与边距拉大,上下边距为10px,左右改为15px。继续修改:

CSS代码:

.box{width:268px; height:58px; padding:10px 15px; border:1px solid #ccc;}

经过“精确”计算后终于得出了如上的CSS结果。一次又一次的不断改版,修改的页面数量越来越大,其中的代码越发复杂,计算量也越发庞大,你只能一边为自己的精确到1px像素级的“专业”功力而沾沾自喜,而后又不得不疲于奔波与各个页面中的宽高计算。就这样一个又一个只有你才能明白的代码出现了!

如何才能摆脱这样的无效代码问题呢?这里使用CSS三权分立模式可谓是最佳解决方案。

CSS三权分立模式的核心在于完全分离width,margin,padding这三个CSS属性,一个class里只能拥有三个属性里的其中一个,而通过增加一个额外标签使得能够通过多个class控制元素的外观,解除三者的耦合。

就如以上问题为例,重写300px宽,80px高的盒子,文字离边有10px间距。使用三权分立模式下的写法为:

Html代码:

 <div class="box">
<div class="roundBox">
市面上我们常常会看到各种各样的设计模式书籍,Java设计模式、C#设计模式、Ruby设计模式等等。
</div>
</div>

CSS代码:

 .box{width:300px; height:80px;}
.box .roundBox{padding:10px;}

这里的宽度、高度、间距的数值同需求完全一致,无需计算。接下来遇到改版问题为Box添加1px颜色为#ccc的边框,只需这么修改:

CSS代码:

 .box{width:300px; height:80px;}
.box .roundBox{padding:10px; border:1px solid #ccc;}

直接为内部标签class上添加边框,单刀直入添加边框属性即可。最后一关如需把文字与边距拉大,上下边距为10px,左右边距为15px。

CSS代码:

.box{width:300px; height:80px;}
.box .roundBox{padding:10px 15px; border:1px solid #ccc;}

注意:上例可以看到padding与border其实在“宽度占有”的性质上是一致的(从某个角度来说把border设置为透明再设置一定宽度就是变相的padding),所以完全可以让这俩个属性写在同一class里,二者性质类同,无需再分离这俩个属性。

我们可以看出使用三权分立模式书写代码的简洁与高效,而且从可读性及维护性上有质的飞跃。唯一多的就是为内部添加一个额外的标签保证padding/border不会与width产生干扰,解除二者的耦合关系,这也是获得可维护性需要付出代价。

在刚才的例子中似乎忽略了另外一个属性——Margin。这里的Margin在三权分立模式中的立场相对于上面的width与padding耦合强度下似乎并没有那么明显,但以我的个人观点上来看:Margin的分离是三权分立模式之中最为重要的一环。为什么Margin在这里如此重要?因为这里可以看出一名前端开发人员功力素养——代码可重用性。

提及代码可重用性,任何一门计算机语言都有所阐述,而CSS代码可重用性对于一名前端来说不亚于对JS重构的重要性。CSS的可重用性会直接影响HTML代码的可复用性。

再回头看如上代码,这里的盒子仅仅是一个页面的一小部分而已。纵观整个网站,不会也不可能出现只需要一个模块的页面。每写一个模块,将来就要和各式各样的页面进行整合、维护、开发。

继续上面例子,现在我们做好了这个模块,开始行进页面的整合。假设这个模块所放的位置同左边的间隔需为10px,同上方模块间隔15px,可能会这么写:

CSS代码:

 .box{width:300px; height:80px; margin:15px 0 0 10px;}
.box .roundBox{padding:10px 15px; border:1px solid #ccc;}

这里的写法原本是没有大过错的,既然提出了Margin分离的重要性,当然不会那么简单的糊弄过去的。记住:你所写的每一个模块的代码将来都会有被复用到另外一个页面(项目)的可能性,甚至于我曾看到过一个模块被几个不同的页面反复重用。

说到这里,继续下去,假设这个模块需要在另外一块地方使用,而那里设计位置要求必须是同上方间隔为0px,离左边5px。内容结构完全一致,遇到这样的情况,你会如何解决?

一般遇到这种情况,第一种做法有人会对原先代码视而不见,直接重写一份新的HTML和CSS。你别说,我还真遇到过。第二种做法或许会这么修改HTML和CSS:

HTML代码:

 <div class="box anotherPlace">
<div class="roundBox">
市面上我们常常会看到各种各样的设计模式书籍,Java设计模式、C#设计模式、Ruby设计模式等等。
</div>
</div>

CSS代码:

 .box{width:300px; height:80px; margin:15px 0 0 10px;}
.box.anotherPlace{margin:0 0 0 5px;}
.box .roundBox{padding:10px 15px; border:1px solid #ccc;}

添加一个结合选择器限定覆盖原先的margin属性,这一类做法是比较聪明的办法,如果这个模块多次重用则添加多个结合选择器即可,我曾今也常使用类似做法来修复各类的Bug。

或许上面的方法的确可以解决类似的问题,但作为一名喜欢没事瞎琢磨点东西的我来说总觉得有什么地方不对。在不断的思考下最终发现这个并非是真正重用,只是一种“修复”,从某个角度上来说,这不过是为这个模块打一个“补丁”。一个又一个结合选择器不就是一个个小的补丁嵌入到不同的页面中去么,依然会有那么多多余的代码会被写入,这并非是理想中的重用。

理想的重用是不添加任何代码,仅仅使用原先的代码就能完全搞定所有的模块,虽然只是理想状态,但我们可以不断的向这个目标靠近。在研究一些CSS库后,发现很多地方同自己的想法不谋而合,其中Margin分离就是其中之一,我们来看看以下代码片段:

CSS代码:

 .m10{margin:10px}
.m15{margin:15px}
.m30{margin:30px}
.mt5{margin-top:5px}
.mt10{margin-top:10px}
.mt15{margin-top:15px}
.mt20{margin-top:20px}
.mt30{margin-top:30px}
.mt50{margin-top:50px}
.mt100{margin-top:100px}
.mb10{margin-bottom:10px}
.mb15{margin-bottom:15px}
.mb20{margin-bottom:20px}
.mb30{margin-bottom:30px}
.mb50{margin-bottom:50px}
.mb100{margin-bottom:100px}
.ml5{margin-left:5px}
.ml10{margin-left:10px}
.ml15{margin-left:15px}
.ml20{margin-left:20px}
.ml30{margin-left:30px}
.ml50{margin-left:50px}
.ml100{margin-left:100px}
.mr5{margin-right:5px}
.mr10{margin-right:10px}
.mr15{margin-right:15px}
.mr20{margin-right:20px}
.mr30{margin-right:30px}
.mr50{margin-right:50px}
.mr100{margin-right:100px}

以上其实就是一个CSS公用文件的一段代码摘抄,其思想引入这些公用的CSS类,单独定义Margin。通过简写命名,即使用头字母来记忆里面的属性,例如:mt15就是margin-top:15px的意思,下面来看我们如何使用它吧。

原先那个模块整合到页面中需要离上15px,离左10px,使用CSS库的做法就是这么写的:

HTML代码:

 <div class="box mt15 ml10">
<div class="roundBox">
市面上我们常常会看到各种各样的设计模式书籍,Java设计模式、C#设计模式、Ruby设计模式等等。
</div>
</div>

CSS代码:

 .box{width:300px; height:80px;}
.box .roundBox{padding:10px 15px; border:1px solid #ccc;}

剔除多余的结合选择器,选择可以高度重用的CSS属性,这里你或许觉得没有多大意义,但如果需要把这个模块放入别的页面呢,就如上面又要放置在同上方间隔为0,离左边5px的地方,此效用开始发挥作用了,直接修改HTML代码,无需任何的CSS的修改,模块的高度复用:

HTML代码:

 <div class="box ml5">
<div class="roundBox">
市面上我们常常会看到各种各样的设计模式书籍,Java设计模式、C#设计模式、Ruby设计模式等等。
</div>
</div>

这样子你还会认为原先的选择器做法是正确的么。个人认为:Margin是阻碍模块重用的最大杀手,因为任何一个组件都或多或少会添加Margin这个属性来间隔开自身同他人的距离,直接拷贝原先代码,则先前适用Margin的属性反而成为了新组件位置的阻碍,及时分离Margin可以有效的解除组件同Margin的耦合,达到重复使用的效果。

这就是三权分立模式的全貌,width,margin,padding这三个属性完全的分离,大大提高的代码的可复用性,可维护性,解除三者的耦合,为将来的开发维护打下坚实的基础,也是CSS设计模式的优势所在。

当然设计模式也有他的二个弊端:

复杂性:获得可维护性往往要付出代价,那就是代码可能会变得更加复杂、更难被新手理解。三权分立模式中的导入公共CSS重用模块,书写多个class合并代替使用单一class都是所需要考虑和规范的。

性能:多数的设计模式对代码的性能会有所拖累,这种拖累可能微不足道,也可能完全不能接受,这取决于项目的具体要求。这里的三权分立模式就需要额外添加一个内部标签来分离padding属性。

实现设计模式比较容易,而懂得应该在什么时候使用什么模式则较为困难。你应该尽量保证所选用的模式就是最恰当的那种,并且不要过度牺牲性能。这对于个人开发甚至于一个团队的开发维护都具有莫大的帮助,个人认为使用三权分立模式是利远大于弊的,希望你也能够了解他的思想与作用,在团队开发中使用他,维护他,三权分立模式现实应用可以帮助你和你的团队开发出更加茁壮的代码。

写了那么多,只希望你能了解到使用合理的CSS设计模式可以帮助我们优化代码,其最终目的是为了CSS开发维护的高效性和可维护性,而其魅力也在于此。谢谢!

CSS设计模式之三权分立模式篇的更多相关文章

  1. CSS设计模式之三权分立模式篇 ( 转)

    转自 海玉的博客 市面上我们常常会看到各种各样的设计模式书籍,Java设计模式.C#设计模式.Ruby设计模式等等.在众多的语言设计模式中我唯独找不到关于CSS设计模式的资料,即使在网上找到类似内容, ...

  2. 设计模式-14 MVC模式

    一 MVC设计模式 MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式,它是一个存在于服务器 表达层的模型,它将应用分开,改变应用之间的高度耦合 MVC设计模式将 ...

  3. JavaScript设计模式之策略模式(学习笔记)

    在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...

  4. C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginator插件和knockout.js完成分页功能 图片在线裁剪和图片上传总结 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi

    C#设计模式总结 一. 设计原则 使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性.并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则.开放封闭原则.里氏代替 ...

  5. Java设计模式-享元模式(Flyweight)

    享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用. FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查 ...

  6. 设计模式:策略模式(Strategy)

    定   义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...

  7. 4. 星际争霸之php设计模式--工厂方法模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  8. C++设计模式——享元模式

    本文版权归果冻说所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.如果这篇文章对你有帮助,你可以请我喝杯咖啡. » 本文链接:http:// ...

  9. 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)

    命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...

随机推荐

  1. 解决使用IIS5.0配置的FTP服务器,客户端浏览器访问时无法获取目录列表的问题。

    我在windows xp sp3下利用iis构架了FTP服务器,允许且只允许匿名用户登陆.但刚开始配置好后,不管是使用命令行模式还是使用浏览器都发现无法访问. 于是怀疑防火墙屏蔽端口所致,果不其然,在 ...

  2. malloc,vmalloc与kmalloc,kfree与vfree的区别和联系

    kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续kmalloc能分配的大小有 ...

  3. html5 placeholder

    placeholder是html5<input>标签的一个属性,placeholder 属性提供可描述输入字段预期值的提示信息(hint).该提示会在输入字段为空时显示,并会在字段获得焦点 ...

  4. Matlab单一变量曲线拟合-cftool

    2.启动曲线拟合工具箱>cftool 3.进入曲线拟合工具箱界面“Curve Fitting tool”(1)点击“Data”按钮,弹出“Data”窗口:(2)利用X data和Y data的下 ...

  5. when does the View.ondraw method get called

    a View's onDraw() is called when: The view is initially drawn Whenever invalidate() is called on the ...

  6. MySQL内置函数

    MySQL中的内置系统函数 用在SELECT语句,以及字句where  order by   having 中UPDTE   DELETE 函数中可以将字段名作为变量来用,变量的值就是这个列对应的每一 ...

  7. Reason we use Camel

    Camel is mainly for integration purpose, in our project we also use it inside the single component t ...

  8. 使用httputils上传图片到服务器

    //创建httpUtils对象 HttpUtils mRegHttpUtils = new HttpUtils(); //图片路径 String path = "/sdcard/Downlo ...

  9. [转]SQLITE3 C语言接口 API 函数简介

    SQLITE3 C语言接口 API 函数简介 说明:本说明文档属作者从接触 SQLite 开始认识的 API 函数的使用方法, 由本人翻译, 不断更新. /* 2012-05-25 */ int sq ...

  10. Magento产品批量导入方法?

    从事外贸的我们在工作中,经常需要添加成千上万个的产品,如果一个一个的去上传,要花费很多时间,有是很让人头痛,那么应该如何实现产品批量上传?如果使用的是Magento系统的话,那么你现在有福利了,因为M ...