Sass 愿景
在最近的一个 CSS 见面会上,我向与会者提问,“有人会在日常的工作流中使用 Sass 吗?”回答结果压倒性的表示肯定——保守谨慎地使用 Sass 已经成为过去式。Sass 正迅速成长为编写 CSS 的标准方式。
这真是难得的好消息!Sass 包含了诸多 CSS 规范未定义的特性,比如变量、控制指令、混合宏等。这些特性赋予了开发者强有力的工具,以应对复杂和持续更迭的样式表。Sass 的灵活性和健壮性,足以实现开发者任何天马行空的创意思维。
令人遗憾的是,虽然 Sass 的使用规模在持续扩大,但生成的 CSS 质量却在不断降低。合理的解释是:Sass 在开发者和样式表之间引入了一层抽象层。过去,我们曾经反复修正 web 标准,现在,我们需要一种方式将其融合入新的开发环境中。目前的问题是,Sass 规范扩展了太多东西,以至于需要持续修正以应对任何 web 标准的改变。实际上,我们需要一种指导思想——适用且独立于 Sass,指导开发者的编程方式。
为了更深入的探讨问题,我们有必要再回顾一次当下面临的困境。
症结
对于 Sass 的诸多特性,最常见的滥用方式就是——选择器的过度嵌套。这里请不要误解我的意思:嵌套选择器确实是非常有用的编程方式,它组织起了局部的代码,使之更易于管理;但是,过度嵌套就是一种灾难了。
举例来说,有时候嵌套会生成冗长的选择器列表,而这会严重影响渲染性能:
body #main .content .left-col .box .heading { font-size: 2em; }
使用如此详细的选择器弊大于利。当未来需要重写级联样式时,开发者就必须使用权值更高的选择器列表。甚至,最终需要借助 !important
的权值 —— 这种写法,上帝都看不下去,严重影响后续的可维护性。
body #main .content .left-col .box .heading [0,1,4,1]
.box .heading [0,0,2,0]
最后,由于选择器依赖于 HTML 文档结构,那么如此冗长的嵌套就会降低样式的可维护性和可移植性。未来,如果我们想要给一个父级不是 leftcol
的 box
,同样设置 heading
样式,我们就需要再写一条样式来实现(合理的开发方案应该是复用这条样式的)。
生成的 CSS 代码质量低下,最大的原因就应该归罪于过度嵌套。其他存在的问题,还包括代码的重复和耦合——有必要再次指出,这些都是使用 Sass 的不合理方式所引起的。那么,我们应该如何更缜密的使用 Sass 呢?
对策
一种方式是创建一些规则,限制和掌控那些存在风险的特性。比如,Mario Ricalde 使用 Inception-inspired 准则限制嵌套:“嵌套永远不要超过四层”。
这些规则往往非常适用于新手,为他们制定了明确的使用权限。但是,一方面这种公认的规则少之又少,另一方面 Sass 规范又在持续扩展。对于每一次新版本发布,都会带来诸多新的特性,并且使用起来会束缚开发者的创造力。单独的几条规则是无法掀起革命性影响的。
我们应该高度重视开发过程中的最佳实践,而不是去整理收集零散的规则。这种最佳实践的核心内容如下所示:
- 编程准则,或者是特定编程语言的编程规范,包含建议的编程风格、最佳实践和方法。
- 框架,或者是标准化编程的文件/文件夹结构,可以用作网站开发的基础。
- 样式指南,或是在线文档,详述了开发网站和应用的所有元素和代码模块。
每个方面各有所长:
- 在一个大型代码库中,编程准则有利于统一团队的编程风格,改善整体代码的可维护性。更多信息请参考 Chris Coyier 的 Sass guidelines。
- 框架兼具实用性和灵活性,同时也将开发过程中的决策压力降到了最低。正如所有的前端开发者所熟知的那样,即使是决定 CSS 的命名方式都会让人感到压力重重。
- 样式指南将编程语言的语法规则和实际生成的效果联系在了一起,指南中使用详细的示例解释了系统中每一个组件的特点。
每个方面也各有难点:
- 编程准则往往是冗杂繁重的,必须持续更新保持准确性。对于新手和缺乏经验的开发者,都存在不小的学习难度。
- 框架往往会让整体代码变得有些臃肿,其灵活性是需要付出一定代价的。
- 样式指南在不同的开发主题中是迥然不同的,往往为了适应特定的上下文环境需要付出一定的修正时间和精力。
不幸的是,虽然这些方法指出了一些使用 Sass 的技巧,但没有解决我们的实际问题。我们使用 Sass 的难点并不是源于语法规范本身,而是我们使用 Sass 的方式。归根结底,Sass 只是一个 CSS 预处理器;所以我们的问题实际上是,处理过程。
那么,我们又该如何处理?
再看症结
每一份工作都或多或少受到人为因素的影响,当我们将工作投入实际生产之后,问题就会被放大出来。我们需要认清,Sass 帮助了我们构建 CSS,但这并不是我们奋斗的终点。实际上,如果 CSS 中引入了变量,那么大多数的事情都要改写,而 Sass 和 CSS 规范也将逐渐融合为一体——这也意味着 Sass 的消亡。
我们实际上所需要的解决方案,并不应该针对代码本身,而应该针对作为开发者的我们。该解决方案需要包含技术准则,以辅助开发者使用 Sass,同时还要深思熟虑放眼未来。所以,我们需要一份了包含了相关意图和目标的公开声明,换言之,一份宣言。
Sass 宣言
当我学会 Sass 之后,我就建立了一些个人准则。时间匆匆,它们形成了宣言的雏形,常常被我用来评估新的特性和技巧——以判断它们是否适用于我的工作流程。当 Sass 羽翼丰满频频被用于我的团队时,这些信条就变得举足轻重了。
我的 Sass 宣言包含了六个信条,或者说是条款,详列如下:
- 输出胜过输入
- 可用胜过依赖
- 易懂胜过简洁
- 稳固胜过反复
- 功能胜过表现
- 一致胜过新奇
虽然每个信条都可能逐步发展为规范建议的条款,但固定不变的运用它们显然是没有意义的。接下来,让我们进一步解析每个信条背后的深层次思考。
1. 输出胜过输入
最终生成的 CSS 的质量和完整性,显然要比预编前的代码更重要。
该信条是其他信条的根本。有必要认清,Sass 只是整个流程的一个处理过程,它转换为 CSS 文件,还要经过浏览器的解析。这可不是说 CSS 需要优雅的格式和极高的可读性(这往往不是关注的重点,尤其是在开发者遵循最佳实践使用压缩的 CSS),但你必须时刻警惕最终的渲染性能。
当你使用了 Sass 规范最新的特性,有必要扪心自问,“这种方式输出的 CSS 是怎样的?” 如果疑惑不清,那么就要看一看源码——可以查看生成的 CSS 代码。深度理解 Sass 和 CSS 相互之间的关系,将有助于你发现潜在的性能瓶颈,并拿出相应的对策优化 Sass 结构。
举例来说,使用 @extend
指令继承选择器中的每个样式。Sass 代码如下:
.box {
background: #eee;
border: 1px solid #ccc;
.heading {
font-size: 2em;
}
}
.box2 {
@extend .box;
padding: 10px;
}
编译结果:
.box, .box2 {
background: #eee;
border: 1px solid #ccc;
}
.box .heading, .box2 .heading {
font-size: 2em;
}
.box2 {
padding: 10px;
}
如你所见,.box2
不仅仅继承了 .box
的样式,还像 .box
一样成为了 .heading
的父级选择器。虽然这只是一个小例子,但如果你不理解 Sass 的生成方式,那么就会生成意想不到的结果。
2. 可用胜过依赖
项目应该具有可移植性,远离对外部的过度依赖。
只要你使用 Sass,那么你就需要引入相关的依赖。最简单的例子就是 Sass 的安装和编译依赖于 Ruby 和 Sass gem。需要牢记的是,使用的依赖越多,做出的妥协越多,就会逐渐丧失了使用 Sass 的带来的收益:团队协作不因噎废食。
举例来说,使用 Sass gem 可以安装很多额外的软件包——它们往往可以完成大量的工作。最常用的框架就要属 Compass 了,此外你还可以安装编写栅格的 gems,以及 Bootstrap 之类的框架。这些 gems 对完成大量的细碎工作非常有用,比如创建一个调色板,或是添加阴影等。
这些 gems 内建了一系列的混合宏,可以被开发者引用到自己的 Sass 文件中。不同于开发者在项目内编写的混合宏,gem 中的混合宏存放在安装目录。gems 在外部被引用的方式,非常类似 Sass 核心功能的使用方式,而且只能通过 @include
指令调用。
这就是 gem 棘手的地方。现在我们假设一种场景,有一支团队正致力于某个项目:团队成员之一,John,决定安装一个 gem 来辅助管理栅格。他安装后就将 gem 引入了项目,并在开发者中使用它。与此同时,另一个团队成员,Mary,下载了项目的最新版本,想要更改一下网站的字体。她下载之后尝试进行编译,却突然编译中断抛出了错误。由于 John 在项目中引入了额外的依赖,而 Mary 的文件中缺少相关的依赖,导致她必须调试错误并下载相关的依赖。
在大型团队中,这种问题屡见不鲜。如果再算上错综复杂的版本和 gem 的内部依赖,处理起来令人崩溃。对于 Ruby 项目,维护统一的开发环境,其最佳实践就是追踪和安装确实需要的 gems 和版本。但最简单的方法,还是尽量避免使用额外的 gems。
免责声明:我最近正在实用 Compass 框架,而且也感觉利大于弊。不过,由于 Sass 规范的建议,我已经开始考虑适时和 Compass 说拜拜了。
3. 易懂胜过简洁
编写 Sass,代码结构要清晰易懂,方便后续开发者的维护工作。
Sass 可以输出严格压缩的 CSS,所以无需手动优化编译前的代码。不同于 CSS 的注释,Sass 中的行内注释并不会出现在最终的 CSS 中。
这对于书写混合宏的文档非常有用,如下所示的注释不会被输出到最终的 CSS 中:
// Force overly long spans of text to truncate, e.g.:
// @include truncate(100%);
// Where $truncation-boundary is a united measurement.
@mixin truncate($truncation-boundary){
max-width:$truncation-boundary;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
无论怎样,一定要考虑 Sass 中的哪些部分需要导入到最终的 CSS 文件中。
4. 稳固胜过反复
不要重复。认清并合理编写复用模式。
在开始任意项目前,明确且尝试定义既定设计中所有不同的模块,是非常明智的做法。这也是编写面向对象的 CSS 的第一步。当然,有一些模式可能无法预测,直到编写了三四遍重复的代码之后才会意识到。
尽快抽象出这些模式,并运用到你的 Sass 中去。
将复用的值设为变量:
$base-font-size: 16px;
$gutter: 1.5em;
将复用的视觉样式设为占位符:
%dotted-border { border: 1px dotted #eee; }
为需要传参的模式编写混合宏:
//transparency for image features
@mixin transparent($color, $alpha) {
$rgba: rgba($color, $alpha);
$ie-hex-str: ie-hex-str($rgba);
background-color: transparent;
background-color: $rgba;
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#{$ie-hex-str},endColorstr=#{$ie-hex-str});
zoom: 1;
}
如果拟采用了这一方法,就会发现,Sass 文件和生成的 CSS 文件,都会更小且易于管理。
5. 功能胜过表现
命名约定,要专注于 HTML 的功能,而不是视觉表现。
使用 Sass 的变量,可以非常简单的主题化网站。然而,我常常会看到这样的代码:
$red-color: #cc3939; //red
$green-color: #2f6b49; //green
将变量与表现效果联系在一起,只有一时的成效。如果设计效果改变了,红色被替换为了其他颜色,就会混淆变量和值的关系。
$red-color: #b32293; //magenta
$green-color: #2f6b49; //green
更好的方式是基于特定的功能来命名这些颜色变量:
$primary-color: #b32293; //magenta
$secondary-color: #2f6b49; //green
Presentational classes with placeholder selectors
当我们不能将视觉样式和功能性类名联系起来的时候,又该如何做呢?假设一个站点中有两个 box,分别是 “Contact” 和 “References”。设计师将两个 box 都设置为了蓝色的边框和背景。我们想最大程度的保持灵活性,但又要尽量缩减冗余的代码。
我们可以在 HTML 中串联这些类名,但需要严格限制:
<div class=“contact-box blue-box”>
<div class=“references-box blue-box”>
牢记,我们要专注于功能而不是表现效果。幸运的是,使用 Sass 的 @extend
指令和占位符,将会使这种方式变得非常简单。
%blue-box {
background: #bac3d6;
border: 1px solid #3f2adf;
}
.contact-box {
@extend %blue-box;
…
}
.references-box {
@extend %blue-box;
…
}
生成结果如下所示,其中并没有 %blue-box 的代码,只有确实需要的代码被实现了。
.contact-box,
.references-box {
background: #bac3d6;
border: 1px solid #3f2adf;
}
这种方式切断了与 HTML 中描述性类名的联系,但在 Sass 文件中仍然可以被理解。为通用样式设计功能性类名,比如 base-box
,将会更有意义。
6. 一致胜过新奇
避免向编译后的 CSS 引入不必要的改动。
如果你希望将 Sass 引入自己的工作流中,且没有新项目的话,你可能会疑问,如何在已有代码库中使用 Sass。Sass 完全支持 CSS,所以最基本的转变就像是将扩展名从 .css
变为 .scss
一样简单。
一旦完成了这种转换,你也许会尝试深入理解代码,或者重构整个文件,化整为零,将选择器嵌套,引入变量和混合宏。对于接替后续开发的人来说,前面的转变一定会带来诸多问题。虽然重构不会影响网站的显式效果,但会生成完全不同的 CSS 文件,而且任何改动的影响都不是孤立的。
转换为 Sass 工作流的最佳方法是按需更新文件。如果你需要改变导航的样式,可以在使用前将其分离为部件。这有助于保护现有的级联样式,并且后续的改动也会非常容易。
展望
最近,我喜欢思考使用 Sass 的难点。这些都是我们使用新的工作方式,所要面临和亟待解决的症结。随着对 Sass 理解的深入,我们最终将会找到合理的答案。
这就是我的愿景,相信这份宣言将有助于调整我们的姿态,继续向前:使用它,改变它,甚至书写自己的宣言。要以专注于书写代码的方式为始,而不是书写的内容。
致谢
http://www.jianshu.com/p/aa1be52204a7
本文翻译自 ALISTAPART 的 A Vision for Our Sass,非常感谢 Felicity Evans 的杰出工作。