LESS、SASS等预处理器给CSS开发带来了语法的灵活和便利,其自己却没有给咱们带来结构化设计思惟。不多有人讨论CSS的架构设计,而不少框架自己,如Bootstrap确实有架构设计思惟做为根基。 要理解这些框架,高效使用这些框架,甚至最后实现本身的框架,必需要了解结构化CSS设计思想。
我不是前端专家,可是我想,是否必定要等成为了专家才能布道?那是否是太晚了。 因此我是做为一个CSS的学习者,给其余CSS学习者分享一下结构化CSS设计的学习心得。 我更多的是一个后端开发者,后端开发的成熟思想一定能给前端带来新鲜血液。javascript
CSS从根原本讲就是一系列的规则,对Html(做为内容标识和呈现)的风格化得一系列规则,所以,语法级别也就是两个重要因素:选择器和应用的风格。 简单而超能。css
一开始,CSS很容易学习,容易上手。当咱们的网站成长为企业级网站时,咱们的CSS开始成为臃肿、重复和难觉得维护的一个混沌 。这种状态在软件开发中被称为Big ball of mud ,更为常见的讲法是面条式代码,是一种缺少良好架构和设计的表象。 解决这个问题的方法和思惟在后台软件开发中已是比较多的讨论也相对成熟。同为软件开发的CSS开发,毋庸置疑,却能够这些借鉴思惟。html
如前所述,CSS只是一系列规则,这是对CSS的初步认识,但咱们不能停留在这个认识。如同,咱们认识到全部物体都由原子组成,可是这个认识并不彻底可以替代化学在分子级别的研究。前端
这些规则必需要进一步分化,各自有不一样做用和角色,不能平面化。表面上同是选择器+风格化的CSS规则,根据它们的业务意义,应该划分为不一样的类别或者方面 (Aspect 参考用词Aspect-oriented programming)。java
这些方面分别是:git
明确的分析你要写的CSS规则属于哪一个方面,在实现上区分这些方面并保持这样的分离,
是往结构化走的重要一步。angularjs
之因此把这两类单独提出来,是由于这是咱们平时理解的css彷佛就彻底只有这两类。 咱们对那些划归这两类的风格没有异议,而对那些属于这两类的反而不太理解。 仍是先简单了解一下这两类自己的范围。github
是应用于基本元素级别的规则,做用于全局统一(默认)的风格。 最好的一个案例:CSS的重置框架reset.css以及bootstrap的改进方案normalize.css, 就彻底是基本风格规则,不包含任何其余类型的规则。 虽然其做用和咱们平时项目中的基本风格不相同,用来理解基本风格的范畴是及其恰当的。编程
在咱们一开始谈前端的结构化时,脑海中第一浮现的设计就是这个分层结构树(或则分形的思惟):
页面 Page => 布局 Layout => 模块 Module => 元素 Element 。bootstrap
一个页面由布局组成,每一个布局局部由一个或多个模块组成,一个模块有n个元素组成,看上去简单而完美,真正的结构化、模块化。 然而,现实世界老是非线性的。在实际的项目中,严格的层次关系设计,遇到了各种“特例”须要打破这个结构。
好比,AngularJS是MVC架构,更准确一些,它是层次结构化MVC, 一个大的MVC又由其它几个粒度更小的MVC组成, 特别是ui-router的嵌套状态和视图把这个结构表达的更清楚。 从设计的思惟上,称之为分形更恰当。
当须要模块与模块之间的通讯和信息交流时,这种结构却不能天然的支持,因而,有一个事件系统创造出来弥补这个缺陷。
之因此有这些“特例”,根本缘由就是分形思惟只适合在模块这一级别,而不能往上扩展到布局和页面界别,也不能往下扩展到元素级别。
布局就是布局,应该做为一个独立的方面存在。
布局规则中,咱们之关注组件之间的相互关系,不关心组件自身的设计,也不关心布局所在的位置。
好比,用list(ol或者ul)作布局用时:
.layout-grid{ margin: 0; padding: 0; list-style-type: none; } .layout-grid > li { display: inline-block; margin: 0 0 10px 10px; }
'list-style-type'和'display'的设置,咱们能够明显看出是布局,'margin'和'padding'彷佛更像基本风格规则。 然而,从使用的目的来看,它们都是用于布局的方面。
这个例子,咱们能够看出对规则的划分不是按CSS的技术特性,而是按业务特性:它们的做用,它们的“含义”。
这个类别含义是很明确清楚,只是强调一下,模块能够放在布局的组件中,也能够放在另一个模块内部,是嵌套的,就是前面说的分形。
咱们通常都用class来定义模块,若是须要用到标签则只能是有语义的标签。如heading系列:
.module > h2{ padding: ...... }
如bootstrap的listgroup
:
<ul class="list-group"> <li class="list-group-item">First item</li> <li class="list-group-item">Second item</li> <li class="list-group-item">Third item</li> </ul>
能够看到,在list-group
以外,它又另外定义了list-group-item
来修饰li
,而不是用如下方式省略掉子类的声明和使用:
.list-group > li { ... }
为何要这样? 能够做为一个思考题放在这。
状态和子模块有时候很类似,却有亮的明显区别:
这是什么意思呢,咱们看看例子:
最经典的案例就是表单数据的有效性,通常都会引入class定义,相似is-valid
;还有就是tab当前激活的状态is-tab-active
等。 前者,会改变表单的布局:增长warning信息;后者,会改变tab模块的显示背景来代表当前tab是被选中的。
而以上两个类也都会由javascript根据用户操做,动态的添加到相应的DOM元素中去。
从状态规则的两个关键词:改变和javascript,咱们能很明显的看出它如其余规则的区别,仍然重点在它的用途和业务含义。 它最重要的一个业务逻辑就是:状态规则与时间相关,这也足以给它一个独立的地位,与模块规则的维度呈正交关系。
正交设计的延伸阅读:
主题是整个网站的风格全面的改变,能够跨项目的才改一次,于是能够在编译阶段进行 如bootstrap的customize用于这种场景;还有就是在一个项目以内也允许用户动态改变。 这些绝对是与其余规则不在一个方面,一定要独立出来。
这类规则会涉及到全部其余类型的规则,如:基本,模块甚至布局和状态,虽然代码量和工做量都较大,概念上却很清楚,这里就再也不展开。
在比较中,咱们看看类别之间的区别。
好比说咱们网页中须要一个表格来显示一些信息,如iPhone 7的产品参数 https://www.apple.com/cn/iphone-7/specs/
为它写一个简单的风格, 没有任何问题:
table{ width: 100%; border-collapse: collapse; border: 1px solid #000; border-width: 1px 1px; } td{ border: 1px solid #000; border-width: 1px 1px; }
以后咱们拿到一个新的需求,一样用表格可是用来比较不一样型号的产品的参数,如 https://www.apple.com/cn/iphone/compare/
为了给客户更好的体验,须要对表格风格作相应的调整,如相间隔的列用不一样的背景色区分,表格的行之间须要实线间隔,而列之间则不要。 并且,这些修改不能影响以前信息表格的风格。
直观的解决方案,咱们引用一个类comparison
来覆盖以前的基本规则 :
.comparision { border-width: 0px 0; } .comparison tr > td:nth-child(even){ background-color: #AAA; } .comparison tr > td { border-width: 1px 0; }
彻底依照新需求,作了三件事情: 1. 去标题的间隔线 2. 去掉了内容行之间的竖线间隔 3. 双列背景灰显。 点击参看在线Demo。
然而,用模块的方式更为清楚,更容易扩展。
首先,与OO中提出基类的思惟相似,这里咱们也提出公共的风格部分:
table { width: 100%; border-collapse: collapse; }
info
而后,为原来的信息表格写出一个分支风格(OO中的子类)
.info { border: 1px solid #000; border-width: 1px 1px; } .info tr > td { border: 1px solid #000; border-width: 1px 1px; }
comparison
最后,为新的比较表格写出另一个分支风格
.comparison tr > td { border: 1px solid #666; border-width: 1px 0; } .comparison tr > td:nth-child(even){ background-color: #AAA; }
点击参看在线Demo
comparison
,从设计的概念和使用的方式能够看到,和模块中的comparison
仍是不一样的,其业务的语义性弱化不少,以致于做为开发者对其命名的准确程度都不太在乎了。 这是一个很很差的倾向。width
和 border-collapse
确确实实是全局的风格,很少一点也很多一点border-width
不会像前面的实现那样,先 td{border-width: 1px 1px;}
而后又 .comparison tr > td {border-width: 1px 0;}
覆盖掉。 能够想象在实际项目中,更多层次的覆盖和更多规则的引入会带来多少的复杂度和差错率,你怎么能准确的判断究竟是那个规则再起做用?由于时间问题,这个案例须要到下次再整理了。
很是强大的思惟,对它自己彷佛有点陌生,当提及递归、全息理论是否更熟悉一些? 这些均可以看做是分形思惟的应用。
有时候又称为面向切面编程,曾经是个煊赫一时的名词,如今好像没怎么提起,不是再也不适用而是思惟已经进入经常使用编程思惟,再也不须要强调了。
和面向方面有些雷同,在这重复也算一个强调吧。 另外,面向方面只能算正交设计的一种实现方式吧。
当你看到这“蓝色 ”两个字时, 你脑子里想到的是“蓝色”仍是“红色”?
语义设计就是要让命名和内容一致,不要扭曲人性。 提高一个层次:咱们要让代码文档化。
覆盖是无结构,典型的“修补”编程法,甚至当不一样需求被引入的先后顺序不一样时,会致使不一样的代码结构,随意性太强。 模块有设计,有业务含义,可维护性很强。