Sass的新特性
本文由大漠根据Hugo Giraudel的《Looking Into the Future of Sass》所译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://davidwalsh.name/future-sass,以及作者相关信息
——作者:Hugo Giraudel
——译者:大漠
Sass是CSS的预处理器,这也是现在最常见讨论的一个话题。Sass最早是2007年由Hampton Catlin提出。Sass到现在已经走了很长的路了,到现在也是最常见的CSS预处理程序。
如果您还不知道什么是Sass,我强烈推荐您阅读一下David Walsh写的这文章和Chris Coyier写的文章。
不管什么情况之下,我将假定您基本都了解Sass,你们当中有一些人甚至是Sass的铁杆粉丝!这些都不先考虑,如果有一篇博客介绍Sass的未来特性,那多好。
Sass3.3即将到来
这一段时间Nathan Weizenbaum和Chris Eppstein正致力于Sass3.3版本开发。截止今天,Sass3.3的alpha版本已经可以使用了(当然其中还有不足),但我相信官方版本将会很快公布(虽然没有具体发布日期)。
那什么是全新的Sass版本呢?里面有很多新东西,他们中的大多数并没有正式宣布做Sass3.3版本,所以我只有猜想:
从一个方面说明,如果你有机会参加2013年CSS峰会,你不要错过它。特别是Chris Eppstein将在峰会上谈论很多很棒的Sass3.3。
Sourcemaps
我认为Sass3.3版本最大的功能是具有Sourcemaps。其实Sourcemaps并不特定于Sass所有。它是一种与语言无关的方式映射到原始的生产代码之中,甚至是编译和压缩。这里有一篇相关的介绍。
在开发者工具中查看一个元素,如果你想编辑样式,比如说是style.css:1
(你的样式文件中的第一行)。你是没有办法可以编辑CSS样式文件,但Sass文件可以。
这基本上是的Sass sourcemaps派上用场。而style.css:1
刚好是component.scss:42
编译出来的,这样不是更好吗?
你使用sourcemaps Sass工作流,可以分五个步骤:
- 你需要使用Chrome28+
- 确认你使用的是Sass3.3 Alpha(
gem install sass --pre
) - 到
chrome://flags
中开启Developer Tools Experiments
- 打开开发者工具,并到
General tab
中选择Enable source maps
- 打开开发者工具,并到
Experiments tab
中选择Support for Sass
- 使用
sass --watch --sourcemap
编译你的样式文件
到此,你的浏览器就可以使用DevTools Sass roucemaps
(.map文件)将你的产品代码映射到你的开发者代码中。你现在可以点击(文件名和样式)和访问你的Sass文件。
注:截至今天,如果你在使用Compass0.13
版本,Sass3.3 Alpha还存在一些严重的问题。你不能使用compass watch
,现在你要使用sass --compass --sourcemap --watch style.scss:style.css
.更多信息可以点击这里查看。
有关于Sass sourcemap功能和开发者工具调试相关介绍,可以阅读《SASS调试》一文。
改善选择器&
你可能已经知道了参考选择器(&
)。这是一个引用当前选择器用于嵌套规则,不能用于根级别嵌套(Sass抛出一个错误)。最简单的使用情况如下:
a {
color: tomato;
&:hover {
color: deepskyblue;
}
}
编译出来的CSS
a {
color: tomato;
}
a:hover {
color: deepskyblue;
}
这是最基本的。他们在这个参考选择器上取得了大量的改进,让我们看看其中的一对。
在选择器中使用&
修复了几个Bug,使用参考选择器在更复杂的选择器上,请参考下面的例子:
$base: ".module";
#{$base} {
&#{$base}-something {
/* ... */
}
}
在本例中,您为您的模块定义某种基本选择器,然后想应用样式到某个元素和派生选择器(.module
和.module-something
)。但按上面的代码写,编译代码时会报错:
error style.scss (Line 4: Invalid CSS after "&#{$base}": expected "{", was "-something {""-something" may only be used at the beginning of a compound selector.)
在Sass下一个版本,我们将能够通过插值和参考选择器混合使用,参考资料。
$base: ".module";
#{$base} {
#{&}#{$base}-something {
/* ... */
}
}
当前,唯一的解决方法是从基本变量中删除.
,并且显式的在选择器中使用:
$base: "module";
.#{$base} {
&.#{$base}-something {
/* ... */
}
}
转译出来的CSS:
.module.module-something {
/* ... */
}
这并不是怎么强大,因为在编码和在前嵌套选择器,迫使在编码中使用类名.
。还是在等待新的&
功能,对吗?
BEM-like语法
有一件事情我真的很期待,扩展选择器&
能够运用在BEM语法上。如果你不是很熟悉BEM语法,我强烈建议你读一下这个帖子。
如果你想了解更多有关于BEM相关的知识,可以点击w3cplus发表的有关于BEM教程。
问题是在使用Sass的嵌套。让我们来看一个简单的例子:
.block {
/* Base stuff */
}
.block__child {
/* Sub-element of block */
}
.block--modifier {
/* Variation of block */
}
这是你想要的。对吗?不幸的是,你不能在嵌套中使用(没有任何嵌套)。如果我们能像下面这样使用:
.block {
/* Base stuff */
&__child {
/* Sub-element of block */
}
&--modifier {
/* Variation of block */
}
}
这将是可怕的。最好的语法,但截至今天,编译上面的代码仍然会报错:
error style.scss (Line 4: Invalid CSS after "&": expected "{", was "__child {""__child" may only be used at the beginning of a compound selector.)
舍得庆幸的是,他们将提高参考选择器在这种语法上的某种程序上的使用(详细参考这里)。快速笔记,我们将不得不使用插入选择器:#{&}__child
。这也没什么大不了的。
绑定父元素
这个例子是Ericam从Susy框架中提出的(参考这里)。他创建了一个mixin
,使用参考选择器来制作了适合IE浏览器的@media
规则:
@mixin respond-to($media, $ie-class:'ie') {
@media (#{$media}) { @content; }
// We need the parent selector here in order to mimic the @media bubbling.
.#{$ie-class} & { @content; }
}
想法很简单:他被称为mixin
,给@media
传递一个有效的值,在大括号中写上所有想要的样式。这些样式将显示在一个@media
块中。此外,通过参考选择器为IE创建另一个样式块。
// This works already.
.container {
@include respond-to('min-width: 30em') {
color: red;
}
}
// This doesn't work.
@include respond-to('min-width: 30em') {
.container { color: red; }
}
从内部调用mixin
选择器工作是很强大的。然而从根标签调用mixin
,Sass会报错(基本规则是不能包含父选择器引用字符&
)。这是因为&
选择器不能用于根一极。
可以理解的。然而它应该可用。这将防止Sass在这种情况之下报错,比如这个例子。希望在新的版本之中能解决这个问题,详细讨论可以参考这里。
改善if()
函数
你听过if()
函数吗?这是一个众所周知的三元判断variable = condition ? true : false
(javasctipr,PHP)。他像下面这样工作:
$a: 10;
$b: if($a > 5, blue, red); // blue
除了一个小问题,这个函数没有条件价值。把它简单,功能的工作原理是这样的:
- 检查条件
- 评估真的结果
- 评估错的结果
- 分配显示结果
当它应该是这样的工作的:
- 检查条件
- 评估显示结果
- 分配显示结果
这看起来好像我很挑剔,但在这种情况下,一个结果不应该被显示。如下面的例子:
@function dosomething($argument) {
$second-item: if(
type-of($argument) == list,
nth($argument, 2),
$argument
);
@return $second-item;
}
// This works like a charm
dosomething( (item-1, item-2) );
// This throws an error
dosomething( only-item );
如果给定的参数是一个列表,那么函数返回列表中的第二个列表项;如果给定的参数不是一个列表,那么函数返回他的参数。不幸的是,这个函数,如果传的参数只是一个就会报错。
这个不应该失败。正常的行为会检查条件,在目前的情况下意识到这是错误的,所以会跳到第三点,甚至没有试图解析第二点。不幸的是if()
解析了一切。
所以他们决定在内置解析器解决if()
问题,详细请参考这里。
行进的操作列表
Sass提供了相当多的函数来操作列表。他们功能都是非常强大的,尤其是join()
函数,可以将一个列表附加到另一个列表中。他们正在考虑通过+
操作符来操作列表,详细请阅读这里。
$a: item-1, item-2, item-3;
$b: item-4, item-5, item-6;
/* Same as
* $c: join($a, $b);
*/
$c: $a + $b; // item-1, item-2, item-3, item-4, item-5, item-6
直到目前这只是一种想像,但尽管如此,我认为这是不错的。现在Crhis甚至还提出使用减号-
来操作列表:
$a: item-1, item-2, item-3;
$b: item-3, item-4, item-1;
$c: $a - $b; // item-1, item-2, item-3, item-4
减号-
操作符应该删除掉第一列中具有的第二列的列表项。这可能是一个强大的功能,当你想要删除你想要删除的。包括false
和null
值:
$a: item-1, false, item-2, null, item-3;
/* Same as
* $a: reject($a, false);
* $a: reject($a, null);
*/
$a: $a - (false, null); // item-1, item-2, item-3
即使这是可能的,但Nathan似乎对此不太感兴趣——需要以唯一的语言来实施,就像Ruby一样,但并不是每个人都是熟悉Ruby。
列表就要介绍完了,我听到一个nth()
函数,可以用来获取列表的第n
个列表项,他可以接受负值,从列表的末尾开始索引列表项,而不是初始值(请参考这里)。为了得到列表中的最后一个列表项,你可以这样操作:
$list: a b c d e;
$last-item: nth($list, length($list)); // Old way
$last-item: nth($list, -1); // New way
一个新函数list-separator()
这是一个很小的飞跃,但我人为做Sass列表成员之一是非常不错的。
他们计算增加一个list-separator()
函数用来决定一个列表中列表项的分隔符号是空格还是逗号(详情请点这里)。使用相当简单:
$a: item-1, item-2, item-3;
$b: item-1 item-2 item-3;
list-separator($a) -> comma
list-separator($b) -> space
函数返回逗号或空格。因为每管委会值在Sass中都被视为一个列表,通过一个单元素值将返回空格。
支持Maps
我花了一些时间来了解这个,他们似乎想让Sass3.3支持map
功能(详细请点这里)。maps
就像一个关联数组。一个A元素在B元素中的列表:
map = {
A: B,
C: D
}
这就是一个map
,类似于JSON
语言。到今天为止,我还不确定在一两层列表中Map
有什么好处。这里有一个模拟Sass列表嵌套的例子:
$map: (
A B,
C D
);
@each $pair in $map {
$first-item : nth($pair, 1);
$second-item : nth($pair, 2);
}
它是非常棒的,也非常有意义。这也是我为什么不理解Sass要增加map
语法。其实我们可以看到一个更好的原因:使语法结构更简单化。
在任何情况下,这是我能找到关于传入的语法:
$map: (
a: b,
c: d
);
@for $key, $value in $map {
/**
* $key is what we called $first-item
* $value is what we called $second-item
*/
}
到现在为止,我所了解的map
就这些,其他的随后会有。有关于Sass的map
功能信息可以点击这里。
改善@for
循环
我们喜欢循环,他功能是强大的。对于某些任务,他可以减少很多所需的代码。Sass提供了三种不同类型的循环:
@for
:让一个变量i
从m
到n
循环@while
:当条件a
为真时循环@each
:从矩阵中遍历每个元素
这些功能都是非常强大的。然而@for
循环有一个问题。你试过一个做递减的@for
循环吗?如下面的例子:
@for $i from 5 through 1 {
/* Do something with $i */
}
别麻烦了,这将会输出什么。甚至不是一个Sass差错。目前,只有按下面的方法来解决这个东西:
@for $i from -5 through -1 {
/* Do something with abs($i) */
}
基本上,你把两个负值,使循环做递增,再者你可以使用abs($i)
来代替$i
,访问你想要的第一个值。
不管怎么说,他们打算在Sass3.3中修复这个bug(详细信息请点击这里)。Chris Eppstein还打算在循环中添加从1到另一个值做递增和递减。大致是这样的:
@for $i from 1 through 10 by 2 {
/* Do something with $i */
}
然而Nathan Weizenbaum似乎没有合理的@for
循环语法来处理这个边缘性的事情,他可以很容易的复制出:
@for $i from 1 through 10 {
@if $i % 2 != 0 {
/* Do something with $i */
}
}
@at-root
规则
好吧,我跟您说实话:这个对于我来说仍是一个谜。这个@at-root
命令需要嵌套块并将其移到根一级。这功能是不错的,但是为什么呢?为什么你不能只是把它写在根一级呢?
我不能找到更多有关于@at-root
规则(除了语法)更多的信息,这里有几个简单的使用例子:
/* Example 1 - Sass */
.foo {
@at-root .bar {
color: gray;
}
}
/* Example 1 - CSS */
.bar { color: gray; }
/* Example 2 - Sass */
.foo {
@at-root .bar &;; {
color: gray;
}
}
/* Example 2 - CSS */
.bar .foo { color: gray; }
/* Example 3 - Sass */
.foo {
@at-root {
.bar {
color: gray;
}
}
}
/* Example 3 - CSS */
.bar { color: gray; }
你知道吗?我不会在这个@at-root
上花太多时间,对于他们将怎么实现这个特性,他们看起来很有自信,但是我没有看到太多用例。在这个问题上,这里有更多的讨论信息。
新的字符串操作函数
当我从Sass的代码分支中看到这些,我意识到他们打算增加一些字符串操作函数来缓解quote(string)
和unquote(string)
函数功能。参考文档请点击这里。从里面我发现五个新的字符串操作函数:
- str_length(string):返回
$string
长度 - str_insert(string,insert,index):从
$string
第$index
插入字符$insert
- str_index(string,substring):返回
$substring
在$string
中第一个位置 - str_slice(string,start_at,end_at = nil):返回从字符
$string
中第$start_at
开始到$end_at
结束的一个新字符串 - to_upper_case(string):将字符
$string
变成大写 - to_lower_case(string):将字符
$string
变成小写
新的@import
功能
最后但并非最不重要的,他们计划重新整一个强大的@import
功能。然而这并不是Sass3.3的功能,Chris打算在Sass4.0加入,除此之外,新版本也有一些新的功能:
- @import-once
- 导入常规的CSS文件
- 命名空间
- 当导入一个文件夹,会自动索引
index.scss
或者index.sass
- 从一个文件夹中导入所有文件(可能吗?)
请注意这只是猜测。从开始讨论@import
开始我就搜集了这个列表。顺便说一下,在没有请教Nathan或者Chris我就已经摆脱了提议。
只导入一次
如果你熟悉PHP或者其他程序语言,你可能知道include_once
命令(一个类似的语法)。这个想法是为了一次只导入一个文件,而且只有一次。
在当前状态下,如果你两次导入相同的文件,会编译出来两次导入的文件内容,这样太不好了。
一个解决方案是Seanofw提出的一个小函数,将一个文件当为参数传入,然后检查文件名,确保文件只被导入一次。
$imported-once-files: ();
@function import-once($name) {
@if index($imported-once-files, $name) {
@return false;
}
$imported-once-files: append($imported-once-files, $name);
@return true;
}
@if import-once("_SharedBaseStuff.scss") {
/* ...declare stuff that will only be imported once... */
}
值得庆幸的是,新的@import
功能将比这个函数更简单(详细信息请点击这里)。
导入常规的CSS文件
当你在一个项目中将CSS样式导入到SCSS或SASS样式中,是件烦人的事情,因为在Sass样式表中直接导航CSS样式,会完全崩溃。所以在Sass样式中能导入常规的CSS样式是很有意义的。
我想导入Normalize.css文件是最好的一个例子。当你使用@import "normalize"
是没办法匹配一个常规的.css
文件。因此你需要先将css
文件转译成scss
或者sass
文件,这样非常的麻烦。
在新的@import
中应该可以直接实现,详细信息请参考这里。
导入默认号
这主要取决于你的结构,你可以在文件夹中管理你的文件。例如在一个helpers
目录中放了一个mixin
,每个文件夹只放一个文件。他提出了一个新的行为,通过index
来导入一个文件夹。
你可以像下面这样操作:
/* Current way of doing */
@import "helpers/clearfix/index";
/* New way of doing */
@import "helpers/clearfix";
这绝对是一个主要特点, 不过我不明白为什么管理的是文件夹而不是直接文件,但我想应该也是可以的。详细信息请参考这里。
从一个文件夹中导入所有文件
同样一个想法,增加一个新的特性,通过导入一个文件夹直接导入这个文件夹中所有文件。这也被称为"Sass Globbing",目前Chris Eppstein写了一个Sass的插件。(我想在Sass4.0会有这个功能)。
他将充许你像下面这样操作:
/* Import all files from folder helpers */
@import "library/helpers/*";
/* Import all folders from folder library */
@import "library/**/*";
但是请注意,文件被导入会是按字母顺序排列的,这可能会破坏你导入文件和你所需要的不一样。
命名空间
一个比较大的事情是,命名空间让CSS丢失。在Sass中一切都在一个全局范围。Kaelig提出一个讨论主题,建议遵循CSS的全局命名空间。
不管怎么样,他们正在考虑通过命名空间来控制@import
(详细信息请参考这里)。这样你就可以有一个更好控制你的文件和你的项目的方法。
因为这是一个很大的特性,还处理讨论当中,没有决定语法如何使用。我猜可能会像下面这样使用:
@import "file";
@import "file-2" as "Module";
.element {
@extend file%placeholder;
@include Module.mixin();
}
这个只是我的想法,可以不把他当一回事。如果您想了解这个问题,请参考这里。
其他功能
大家先别急于离开哟,下面还有一些很有意思的东西。我在Sass代码分支中发现其他一些很酷的东西!
独特的ID
原来有一个unique-id()
函数返回一个独特的随机标识符(运用于Sass的作用域)作为一个不带引号的字符串。基于我对Ruby代码的理解,它返加一个9个字符串长的字母数字。比如“u214ab34e”。
我还不太确定这种功能主要用途是什么,然而,这可能是一个方法,用来获得一个随机数,让我试试:
@function parse-unique-id($value) {
$letters: a b c d e f g h i j k l m n o p q r s t u v w x y z;
$value: unquote("");
@for $i from 1 through str-length($value) {
$letter: str-slice($value, $i, $i + 1);
@if index($letter, $letters) == false {
$value: str-insert($value, $letter, str-length($value) + 1);
}
}
@return $value;
}
$number: parse-unique-id(unique-id());
如果你想测试这段代码,他是无法编译的,即使是最新的版本,因为它依赖于输入功能。基本上,它会检查unique-id()
每个字符的值。如果这是一个字母,它会删除它。
他会给你一个0~9999999随机数,任何你想要的。
存在的检查
我发现几个功能,我认为主要由内部传动装置或者Sass库(详细请参考这里)。基本上他们允许你测试是否一个变量、函数或者是否存在当前状态。
- variable_exists(named)
- global_variable_exists(named)
- function_exists(named)
- mixin_exists(named)
似乎也存在一个feature_exists(named)
函数,该函数是为了检查当前Sass运行时是否存在一个功能。我想它将用于未来的版本,用来检查一个给定的特性是否存在与否。
总结
好吧,我想说的就是这些,请记住大部分的东西都来自于 Github的问题讨论。没有日期或保证任何版本/特性宣布。
如果你对Sass的一些新特性感兴趣,我推荐你阅读官方的有关信息。
非常感谢您的阅读,下回再见!
译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!
如需转载烦请注明出处: