这两天窝在家里又看了本CSS相关的书:《CSS重构:样式表性能调优》。重构是指在不改变代码行为的前提下,重写代码,使其更加简洁、易于复用。
这本书读起来比较快,可挑自己感兴趣的读,前面三章是基础知识的介绍,都了解的话可直接跳过。第四章是为样式分类,我比较感兴趣的是第四章(测试)和第五章(代码的组织和重构策略)。
一、测试
测试时需要考虑很多因素,其中包括以下几点:
1、正在用什么浏览器测试网页?
2、如何在不同的操作系统上测试各种各样的浏览器?
3、正在多大的窗口浏览网页?
4、如何快速测试大量网页?
5、如何验证你所看到的效果是正确的?
6、如果你无法获得某些设备,如何测试网站在这些设备上的效果?
1)测试多个浏览器
最常用的测试 CSS 在不同浏览器中显示效果的方法是人工测试,主流浏览器包括Chrome、Firefox、Safari、Microsoft Edge等。
为了测试 CSS 在移动端的效果,需要从合适的应用市场下载适合于设备的各种浏览器。
1、要用iOS系统的Safari浏览器测试, 可以使用iOS原生设备或Xcode的iOS模拟器。
2、安卓设备可以用 Android Studio 的模拟器测试。
2)第三方测试服务
除了自己测试,还可以使用第三方提供的种类丰富的测试服务。它们能够满足测试网站在任意浏览器或其他配置环境中的效果的需求,同时还提供类似共享测试阶段信息、人工测试多种浏览器和截图等功能。
下面列出的几种第三方服务,有些可免费试用一段时间或提供几种级别的免费服务:
4、Litmus
3)视觉回归测试
视觉回归测试是一种测试方法,它通过比较作为基准的用户界面图像和开发过程同一用户界面的图像,来检测不符合预期的改动(回归)。视觉回归测试非常耗时,因为需要测试的浏览器很多,一有改动就进行这种测试,测试工作量很大。此外,肉眼难以确定元素在空间位置上的变化。视觉回归测试有如下技巧:
1、测试重要的点,例如一旦基础样式定义好,就不大可能因为改动它们而引入错误;但是更为复杂、更加脆弱的可用性组件则是需要重点测试的。
2、保持足够细致的粒度,每次只测单个组件。
3、用多种浏览器进行视觉回归测试,因为不同浏览器之间可能存在不一致现象,即不要尝试比较不同浏览器的截图。
Gemini项目是Yandex团队开发的视觉回归测试工具。使用该工具,可以编写脚本,自动截取网站在主流浏览器中的截图,然后将其与基准图像比较,不同之处将以高亮形式标记出来。
除了 Gemini, 还有多种视觉回归测试工具, 其中最常用的两个是Wraith和PhantomCSS。前者由BBC开发,后者由Huddle公司的James Cryer带领开发团队编写,它们能打开网站,能用PhantomJS(基于WebKit的无头浏览器)或 SlimerJS(基于Gecko的无头浏览器)截图,能对比网页现有元素的截图跟元素基准图像之间的差异。
二、维护代码
代码的测试跟编写同等重要。不断维护已有代码,提高其质量,其重要性不亚于编写新代码。
1)编码规范
编码规范是指将良好的代码编写方法记录下来形成指南,以鼓励团队所有成员以相同的方式编写代码。CSS 编码规范通常指定了注释、格式、命名和选择器用法方面的规范,其详略程度可根据实际情况自行调整。
(1)注释
A. 应该在每个文件的开头添加注释,说明文件的内容。
/** * 该文件包含选项卡组的样式。 * 选项卡组应仅包含拥有tab类的元素。 */
B. 易于混淆的属性,应用注释予以说明。
.tab-group-flush { display: block; margin-left: -12px; /* 清除父容器的padding值 */ margin-right: -12px; /* 清除父容器的padding值 */ }
(2)格式
A. 规则集应该满足下列要求。
1、有多个属性时,每个属性占一行。
2、规则集声明块中的每条声明缩进 4 个空格。
.selector { property1: value; property2: value; }
B. 声明语句应该满足下列要求。
1、冒号后面加1个空格。
2、必须以分号结尾。
.selector { property1: value; }
C. background-position各个属性值不同时,可以将两个属性值放在一行。
.selector1 { background-position: 0 0; } .selector2 { background-position: 0 -10px; } .selector3 { background-position: 0 -10px; }
D. 规则集和声明末尾的空格必须删除。
(3)选择器命名规范
A. 只允许使用小写字母。
.selector {}
B. 包含多个单词的选择器必须使用脊柱状形式(用连字符连接单词)。
.selector-with-multiple-words {}
C. 禁止用ID为元素添加样式,应该使用类。
.element-to-style {}
D. 用JavaScript修改样式(不管用什么框架),都必须通过增加或删除CSS类来完成。
/** * 正确:用 JavaScript 为元素添加类,修改元素的样式。 */ $(".js-menu-item").on("click", function(e) { $(this).addClass("highlighted"); });
E. 用作 JavaScript 选择器的类和 ID ,必须添加 js- 前缀,并严禁在样式表中使用。
/** * 正确:在JavaScript中,用专门用作JavaScript选择器的类选择元素。 */ $(".js-menu-item").on("click", function() { $(this).addClass("highlighted"); });
F. 必须使用有意义的类名。
/* 正确:类的命名有意义且描述清楚。 */ .resident {}
G. 类名必须描述为什么元素添加样式,而不是怎样添加样式。
/* 正确:类的命名描述的是为什么元素添加样式。 */ .sidebar-important {}
(4)属性
A. 属性的简写形式只可用于border、margin和padding。
/* 正确:仅border属性使用了简写形式。 */ .selector { border: 1px solid #000000; font-family: Arial, sans-serif; font-size: 12px; }
B. 属性必须按照字母顺序排列。
.selector { border: 1px solid #000000; margin: 24px; padding: 12px; }
C. 属性值为0时,必须省略单位。
.selector { border: 1px solid #000000; margin: 0; padding: 0; }
更多编码规范方面的启示,请见下面这些规范:
2、18F前端指南
2)模式库
模式库 (有时也称样式指南)是网站使用的一组用户界面模式,它展示了每种模式相关的重要信息,其中包括以下几点:
1、何时(不)使用模式的指导。
2、解释模式使用方式的示例代码。
3、使用某一模式而不用另一模式的原因。
模式库有如下几个优点。
1、首先,模式库将网站所有组件汇集到一起。参与项目的所有成员都能了解到搭建网站的各个模块,确保他们熟悉其背后的原理。让每个人都熟悉一组可复用的模式,还能加快开发速度,因为开发新项目时,无需从头重新开发这些构建模块。
2、模式库将所有组件汇集到一起,还有助于保证用户界面的一致性。使用模式库对设计团队也能起到帮助作用,因为需要修改组件的样式时,在现有模式的基础上做修改即可。模式库为设计工作提供约束条件,鼓励设计师在现有模式的基础上设计新的元素,这进一步强化了用户界面应该保持一致的设计理念。
3、最后,模式库将网站的所有组件都汇集到一起,使识别不一致的组件变得更加容易。编写的新代码可能影响到用户界面效果,在提交代码之前,借助模式库,可从视觉上快速识别错误。修改模式后,若网页看起来有问题,有了模式库,诊断问题将更加容易,因为模式库是模式的最简单应用形式。
Yelp和MailChimp的模式库在各个方面都做得非常出色。关于模式库的更多资源,请见styleguides.io, 该网站提供相关示例、文章、图书和播客。
三、代码的组织和重构策略
1)按照样式从最不精确到最精确组织 CSS
CSS 样式根据选择器的特指度和样式的顺序产生作用。因此,有必要按照样式产生作用的顺序组织 CSS 代码。
1、通用样式,用来设定基准,以消除不同浏览器之间的不一致性。
2、基础样式,为网站的所有元素提供基本的样式。留白( margin、padding和line-height等)、字体及其大小等样式。
3、组件及其容器的样式,满足网站范围内的大多数应用场景,样式上的任何调整都应该交由父容器处理。
4、结构化样式,包括组件及其容器的样式。 该类样式用来创建网页的布局,并常用于定义尺寸。
5、功能性样式,所有样式中最精确的样式。 JavaScript所使用的添加 !important 语句的类就属于这类样式,其他为满足单一目的而实现的样式也属于该类。
6、浏览器特定样式(如果一定需要),只对特定的浏览器生效。通常不够优雅,因此不再使用时,一定要将其删除。
按照以上顺序添加 CSS, 随着声明块选择器的精确度提高,更为复杂的选择器将与已经添加的更加宽泛的选择器区分开来。
2)多个文件还是一个大文件
代码的组织方式有两种,即将代码置于多个文件或只用一个大文件。代码放置位置要易于开发人员查找,这一点很重要,并且同样非常重要的是,这样做有助于网站快速加载以满足终端用户的需要。
1、尽可能地使需要下载的 CSS 文件缩小,以便提高加载速度。
2、小型项目用一个 CSS 文件完全可以接受,合理地将 CSS 文件内容划分为几个大块,每个大块下再恰当安排小块内容,并合理添加注释。
/** * 通用样式 * --------------------------------------------- */ /** * 基础样式 * --------------------------------------------- */ /* 基础样式:表单 */ /* 基础样式:标题 */ /* 基础样式:图像 */
3、开发网站时使用多个 CSS 文件,各个文件所包含的样式可以分别服务于网站的某一部分功能,从而可以避免将 CSS 添加到不恰当的位置。
|-css/ | |-normalizing-styles | | |- normalize.css | | | |-base-styles | | |- forms.css | | |- headings.css | | |- images.css | | |- lists.css | | |- tables.css | | |- etc. | | | |-component-styles | | |- alerts.css | | |- buttons.css | | |- carousel.css | | |- dropdowns.css | | |- modals.css | | |- etc. | | | |- structural-styles | | |- layout-checkout.css | | |- layout-sidebar.css | | |- layout-primary.css | | |- layout-settings.css | | |- etc. | | | |- utility-styles | | |- utility.css | | | |- browser-specific-styles | | |-ie8.css
3)重构前审查CSS
从以下更高的角度来审查 CSS,将非常有助于重构:
1、所用到的属性列表
2、使用某一特定属性的声明块列表
3、使用的颜色数量
4、使用的最高和最低特指度
5、拥有最高和最低特指度的选择器
6、选择器的长度
CSS Dig是 Google Chrome 浏览器的一款免费插件, 你可以用它获取到以上信息。
4)重构策略
条件允许的话, 应该只对你能够维护的小块代码进行重构,并做到经常评审和发布。
1、保持规则集结构的一致性可以使开发更容易。请确定好声明块的格式和声明语句的顺序。 每条声明语句可以各占一行,并尽可能按照字母顺序排列。
2、删除僵尸代码,僵尸代码是指存在但没有使用的代码,包括没有使用的声明块、重复的声明块和声明语句。
3、分离CSS和JavaScript,搜索JavaScript代码, 找到选取元素的位置, 然后在选择器前面添加 js- ,同时将该选择器添加到HTML代码中定义该元素的位置。
4、分离基础样式,将基础样式分为以下不同类别:
- 标题(<h1>–<h6>)
- 文本(例如:<p>、<span>和<code>)
- 超链接(<a>)
- 列表(<dl>、<ol>和<ul>)
- 表格(例如:<table>、<thead>、<tbody>、<tfoot>、<tr>和<td>)
- 表单(例如:<form>、<legend>、<fieldset>、<input>和<button>)
- 媒体(例如:<audio>、<object>和<video>)
分完类之后,你可以用 CSS Dig寻找某一特定类别的选择器。找到选择器的使用频率和使用位置之后,你可以对它们进行比较,看看哪个属性更常用。如果一个类型选择器单独使用,且只用过一次,就可以放心将其删除。然而,如果一个类型选择器用过多次,请按以下步骤重构。
- 对于样式要予以重构的类型选择器,在基础样式中新建一条规则集。
- 从所有用到该类型选择器的地方找到最常用的属性,将其添加到新规则集中。
- 从其他规则集删除重复的属性,因为它们可以继承新定义的基础样式。
5、删除冗余的ID,对拥有多个 ID 的选择器进行重构时,首先可以将最右侧 ID 左边的一切统统删除。因为同一个ID在一个网页最多只能使用一次,一个选择器包含多个 ID 实属冗余。
6、将ID转化为类,该过程虽然要花费一定时间,但是最终得到的 CSS 特指度更低、更易于复用。将 ID 改为类时,请记得使用意义明确的类名,切记不要使用晦涩或过于精确的名称。如果解决某一特定问题需要改动大量的选择器,那么最好不要将 ID 转化为类,而是等将更精确样式选择器的特指度降低之后,再来重构这部分代码。
7、区分功能性样式,功能性样式是唯一应该使用 !important 声明的样式。若不得不使用 !important 的样式,且用途单一(如隐藏元素),应将其作为功能样式写到样式表的同一地方。在拼接 CSS 时功能样式应该置于 CSS 文件的底部,因此整合 CSS 文件时,一定要恰当安排功能性样式的位置。
8、定义可复用组件,可以从经常重复使用的界面模式(例如:选项卡)着手,花点时间调研网站什么地方用到了该模式。 请记录该模式的各种变体, 并判断它们是合乎规范的, 还是由于不一致的CSS 而导致的。
9、删除行内CSS和过于模块化的类,两种行为应该同时进行, 因为它们在本质上是相同的, 只不过用 style 属性添加的行内 CSS 其特指度更高,除非用 !important 声明覆盖了行内样式。删除行内CSS和过于模块化的类(每个类应用一种样式,它们总是需要一起使用,如下代码所示)都应该晚些重构。
<h1 class="font-bold uppercase blue-text margin-bottom-large no-padding"> Too Many CSS Classes </h1>
10、隔离面向特定浏览器的CSS样式,这些代码很容易污染其他 CSS, 因此我们需要对其进行区分。但是在隔离之前, 请记得查看网站的流量来源,以判断是否能够放弃支持这些浏览器。
5)评估重构是否成功
下面是重构完成之后如何评价是否成功的一些想法。 其中一些建议,比如检查文件的大小,在重构之前也应要做,以便与重构之后进行比较。
1、你的网站崩溃了吗?
判断代码重构之后成功与否的首要的、最明显的方式是确认网站的行为是否倒退。如果经过彻底的视觉效果检测之后没有发现视觉效果方面的问题,那么接下来你需要考虑其他方面。
- 低耦合度,通过创建可复用组件和用容器包裹组件,你可以实现 CSS 和 HTML 的分离。虽然一定程度的耦合度一直存在,但是还是要避免使用过于复杂的选择器。
- 低特指度,选择器的特指度指标可用于度量代码库的特指度,以判断代码库是否包含大量高特指度的选择器,这些选择器将增加代码的维护成本。
- 更少的文件数量和更小的文件,拼接可以减少需要下载的文件数量,压缩可以删除多余字符以减小文件体积。
2、UI Bug数
一旦你开始重构 CSS 并遵循编码标准,因代码凌乱或代码重复而引入的 UI bug 数量应该会减少。软件 bug 是不可避免的(网站开发人员可能引入 bug, 浏览器厂商可能引入浏览器问题),但是使用模式库和执行视觉效果测试百试不爽,它们能够帮你更快地检测和诊断这些问题。
3、减少开发和测试时间
将 CSS 以符合逻辑的方式分成多个文件,确立了编码标准,并创建了用户界面模式库之后,应该能够较以往更快地构建和维护用户界面了。除了降低开发时间,如果你能熟练使用正确的工具,你也许还注意到你可以更快地测试界面。