LESS、SASS等预处理器给CSS开发带来了语法的灵活和便利,其本身却没有给我们带来结构化设计思维。很少有人讨论CSS的架构设计,而很多框架本身,如Bootstrap确实有架构设计思维作为根基。 要理解这些框架,高效使用这些框架,甚至最后实现自己的框架,必须要了解结构化CSS设计思想。

我不是前端专家,但是我想,是否一定要等成为了专家才能布道?那是不是太晚了。 所以我是作为一个CSS的学习者,给其他CSS学习者分享一下结构化CSS设计的学习心得。 我更多的是一个后端开发者,后端开发的成熟思想必定能给前端带来新鲜血液。

前言

CSS从根本来讲就是一系列的规则,对Html(作为内容标识和呈现)的风格化得一系列规则,因此,语法级别也就是两个重要因素:选择器和应用的风格。 简单而超能。

一开始,CSS很容易学习,容易上手。当我们的网站成长为企业级网站时,我们的CSS开始成为臃肿、重复和难以为维护的一个混沌 。这种状态在软件开发中被称为Big ball of mud ,更为常见的讲法是面条式代码,是一种缺乏良好架构和设计的表象。 解决这个问题的方法和思维在后台软件开发中已经是比较多的讨论也相对成熟。同为软件开发的CSS开发,毋庸置疑,却可以这些借鉴思维。

面向方面

如前所述,CSS只是一系列规则,这是对CSS的初步认识,但我们不能停留在这个认识。如同,我们认识到所有物体都由原子组成,但是这个认识并不完全能够替代化学在分子级别的研究。

这些规则必须要进一步分化,各自有不同作用和角色,不能平面化。表面上同是选择器+风格化的CSS规则,根据它们的业务意义,应该划分为不同的类别或者方面 (Aspect 参考用词Aspect-oriented programming)。

这些方面分别是:

  • 基本(元素)
  • 布局
  • 模块
  • 状态
  • 主题

明确的分析你要写的CSS规则属于哪个方面,在实现上区分这些方面并保持这样的分离,

是往结构化走的重要一步。

基本(元素)规则和布局规则

之所以把这两类单独提出来,是因为这是我们平时理解的css似乎就完全只有这两类。 我们对那些划归这两类的风格没有异议,而对那些属于这两类的反而不太理解。 还是先简单了解一下这两类本身的范围。

基本规则

是应用于基本元素级别的规则,作用于全局统一(默认)的风格。 最好的一个案例:CSS的重置框架reset.css以及bootstrap的改进方案normalize.css, 就完全是基本风格规则,不包含任何其他类型的规则。 虽然其作用和我们平时项目中的基本风格不相同,用来理解基本风格的范畴是及其恰当的。

布局规则

在我们一开始谈前端的结构化时,脑海中第一浮现的设计就是这个分层结构树(或则分形的思维):

页面 Page => 布局 Layout => 模块 Module => 元素 Element 。

一个页面由布局组成,每个布局局部由一个或多个模块组成,一个模块有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和语义标签

我们一般都用class来定义模块,如果需要用到标签则只能是有语义的标签。如heading系列:

.module > h2{
padding: ......
}

用subclass定义嵌套元素风格

如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 {

	...
}

为什么要这样? 可以作为一个思考题放在这。

状态规则

状态和子模块有时候很相似,却有亮的明显区别:

0. 状态改变布局风格或模块风格

0. 状态大部分时候和Javascript相联系

这是什么意思呢,我们看看例子:

最经典的案例就是表单数据的有效性,一般都会引入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还是不同的,其业务的语义性弱化很多,以至于作为开发者对其命名的准确程度都不太在意了。 这是一个很不好的倾向。
  • 基本风格的widthborder-collapse确确实实是全局的风格,不多一点也不少一点
  • 结构非常清晰,风格之间没有复杂的覆盖关系: 比如比较表格中的border-width不会像前面的实现那样,先 td{border-width: 1px 1px;} 然后又 .comparison tr > td {border-width: 1px 0;}覆盖掉。 可以想象在实际项目中,更多层次的覆盖和更多规则的引入会带来多少的复杂度和差错率,你怎么能准确的判断到底是那个规则再起作用?

状态规则和模块规则的正交案例

因为时间问题,这个案例需要到下次再整理了。

设计思维回顾:

分形:

非常强大的思维,对它本身似乎有点陌生,当说起递归、全息理论是否更熟悉一些? 这些都可以看作是分形思维的应用。

面向方面编程:

有时候又称为面向切面编程,曾经是个炙手可热的名词,现在好像没怎么提起,不是不再适用而是思维已经进入常用编程思维,不再需要强调了。

正交设计

和面向方面有些雷同,在这重复也算一个强调吧。 另外,面向方面只能算正交设计的一种实现方式吧。

语义性

当你看到这“蓝色 ”两个字时, 你脑子里想到的是“蓝色”还是“红色”?

语义设计就是要让命名和内容一致,不要扭曲人性。 提升一个层次:我们要让代码文档化。

覆盖和模块

覆盖是无结构,典型的“修补”编程法,甚至当不同需求被引入的前后顺序不同时,会导致不同的代码结构,随意性太强。 模块有设计,有业务含义,可维护性很强。

结构化CSS设计思维的更多相关文章

  1. 你真的了解字典(Dictionary)吗? C# Memory Cache 踩坑记录 .net 泛型 结构化CSS设计思维 WinForm POST上传与后台接收 高效实用的.NET开源项目 .net 笔试面试总结(3) .net 笔试面试总结(2) 依赖注入 C# RSA 加密 C#与Java AES 加密解密

    你真的了解字典(Dictionary)吗?   从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.为了便于描述,我把上面 ...

  2. 软工+C(2017第5期) 工具和结构化

    // 上一篇:Alpha/Beta换人 // 下一篇:最近发展区/脚手架 工具/轮子 软件工程/计算机相关专业的一个特点是会使用到众多的工具,工具的使用是从程序猿进化到程序员的一个关键要素.软件工程师 ...

  3. 软工+C(5): 工具和结构化(重构中, part 1...)

    // 上一篇:Alpha/Beta换人 // 下一篇:最近发展区/脚手架 目录: ** 0x01 讨论:工具/轮子 ** 0x02 讨论:结构/演进 ** 0x03 讨论:行为/活动 ** 0x04 ...

  4. Structured Streaming Programming Guide结构化流编程指南

    目录 Overview Quick Example Programming Model Basic Concepts Handling Event-time and Late Data Fault T ...

  5. 有效的结构化思维训练,MECE分析法

    MECE原则,表达精准分类与全面性的有效利器 结构化思维的本质就是逻辑,其目的在于对问题的思考更完整.更有条理,它帮助我们一个一个找到线头,理清思路,探求事物之间的相互联系.MECE分析法是一种结构化 ...

  6. css 为元素选择器,css目标状态伪类,结构化选择器,多媒体选择器,清除表默认样式、属性选择器

    伪元素选择器 :before 和 :after 添加的位置 :before --- 第一个子节点 :after --- 最后一个子节点 特点 1.默认是 inline 元素 2.必须包含 conten ...

  7. 工作中的Buff加成-结构化思考力:第一章:认识结构化思维及其作用

    一:引言 为了更好的说明结构思考力,我们先来做几个小测试. PS:如果你能做到,请留言,因为我要和你交好友,因为你是人才啊,可以挖一挖,挖到我的公司中. 第一个测试:请在三秒内记住下列数字.数字顺序不 ...

  8. CSS 背景色变化 结构化伪类的练习

    CSS3的nth-child() 选择器(兼容性不好),在做表格偶数行变色的时候,我通常在绑定的时候,做一个js判断,来加一个css,从而使表格偶数行和奇数行颜色不一样.这样的兼容性很好. 但是最近在 ...

  9. XHTML 结构化:使用 XHTML 重构网站

    http://www.w3school.com.cn/xhtml/xhtml_structural_01.asp 我们曾经为本节撰写的标题是:"XHTML : 简单的规则,容易的方针.&qu ...

随机推荐

  1. js:通过正则匹配获取页面的url中的参数

    简介:获取页面参数 原生js: //通过正则匹配获取当前页面的url中的参数 function getUrlParam(name){ var reg = new RegExp("(^|&am ...

  2. HYML / CSS和Javascript 部分

    1  CSS实现垂直水平居中 HTML结构: <div class="wrapper"> <div class="content">&l ...

  3. 【uwp】浅谈China Daily中数据同步到One Drive的实现

    新版China Daily与旧版相比新增了数据同步的功能,那这个功能具体是如何实现的呢,现在让我们来一起看看. 1.注册应用 开发者中心的应用注册就不用多说了(https://developer.mi ...

  4. APP反编译第一课《如何找到核心代码》

    相信很多人都应该会去接触APP反编译,本小七给大家带来入门级别套路,自己也在慢慢摸索学习,一起成长吧.第一步,反编译需要的工具有:一.java环境,其实这里你只要安装了burp就不用管这个的二.apk ...

  5. CSS vertical-align属性

    之前也经常用到vertical-align进行垂直居中对齐,突然发现其中的一些属性值根本就没使用过,也不清楚效果,将今天的研究成果记录下. vertical-align 属性 下表是w3c上列举的属性 ...

  6. sdkman安装

    软件开发工具管理包(Software Development Kit Manager,简称SDKMAN) 用来管理多个版本的开发环境的工具.提供命令行来安装.切换.删除.列出候选版本. 官网地址:ht ...

  7. 使用CSS3中的input标签与lable标签组合实现banner图的切换

    在做网页时,我们经常可以碰到banner图的切换.对于那些JS薄弱的同学来说,这就很尴尬了.今天,我来告诉大家如何使用CSS做出banner图切换的效果. 这里,用到了lable标签与input的组合 ...

  8. Brief introduction to Java String Split 【简单介绍下Java String Split】

    Split is a common function in Java. It split a full string to an array based on delimeter. For examp ...

  9. .Net程序员学用Oracle系列(24):数据字典、死锁

    1.静态数据字典 1.1.实用静态数据字典 1.2.运用静态数据字典 2.动态数据字典 2.1.实用动态性能视图 2.2.运用动态性能视图 3.死锁 3.1.定位死锁 3.2.解锁方法 3.3.强制删 ...

  10. TCP协议总结

    TCP的特性 TCP提供一种面向连接的.可靠的字节流服务 在一个TCP连接中,仅有两方进行彼此通信.广播和多播不能用于TCP TCP使用校验和,确认和重传机制来保证可靠传输 TCP给数据分节进行排序, ...