在写CSS的时候我们会发现,为了兼容浏览器等原因,我们往往需要写很多冗余的代码,CSS预处理器就是为了解决CSS的这些问题,简化CSS代码的编写。
目前最主流的CSS预处理器是LESS、SASS和Stylus,最近花了几天时间学习并使用了它们,于是就想对这三个预处理器做个比较。根据这三种语言的特性,主要从以下几个方面进行讨论:
- 基本语法
- 变量
- 嵌套
- 混入(mixin)
- 继承
- 函数
- @import
- 运算符
- 逻辑控制
基本语法
LESS的基本语法和CSS差不多,SASS和Stylus都可以利用缩进代替花括号,并且空格有重要的意义。SASS保存为”.sass”是缩进格式,保存为”.scss”是非缩进格式。SASS一般使用”.scss”扩展名。LESS的扩展名为”.less”,Stylus的扩展名为”.styl”。
注意:SASS依赖于Ruby,安装前必须先安装Ruby。
LESS & SCSS:
| 1 2 3
 | ul {     list-style: none; }
 | 
SASS:
Stylus:
注意:以下SASS代码都以扩展名为”.scss”的方式书写。
变量
CSS预处理器中可以定义变量,并且可以在样式表中使用,变量类型没有限制,这样就可以一定程度上减少CSS中无法避免的重复问题。
LESS变量名必须以@符号开头,变量名和变量值之间以冒号隔开。有个问题是@规则在CSS中算是一种原生的扩展方式,变量名用@开头很可能会和以后CSS中的新@规则冲突。
| 1 2 3 4
 | @orange: #feb914; header {     background-color: @orange; }
 | 
SASS变量名必须以$开始,变量名和变量值之间以冒号隔开。
| 1 2 3 4
 | $orange: #feb914; header {     background-color: $orange; }
 | 
Stylus对变量名没有任何限定,变量名与变量值之间可以用冒号、空格和等号隔开。
| 1 2 3
 | bgorange = #feb914; header     background-color bgorange
 | 
上面三种不同的写法都会产生相同的结果:
| 1 2 3
 | header {     background-color: #feb914; }
 | 
Stylus还有一个独特功能,它不需要分配值给变量就可以定义引用属性。如下:
| 1 2 3 4 5 6 7 8
 | #logo     position: absolute     top: 50%     left: 50%     width: w = 150px     height: h = 80px     margin-left: -(w / 2)     margin-top: -(h / 2)
 | 
| 1 2 3 4 5 6 7 8
 | #logo     position: absolute     top: 50%     left: 50%     width: 150px     height: 80px     margin-left: -(@width / 2)     margin-top: -(@height / 2)
 | 
变量作用域
三种预处理器中定义的变量都是有作用域的,查找变量的顺序是先在局部定义中查找,如果找不到,则逐级向上查找。
如果我们在代码中重写某个已经定义的变量,Less的处理逻辑和其他两个有区别。Less中,这个行为叫懒加载(Lazy Loading)。注意Less中所有变量的计算,都是以这个变量最后一次被定义的值为准。
LESS:
| 1 2 3 4 5 6 7 8
 | @size: 40px; .content {     width: @size; } @size: 60px; .container {     width: @size; }
 | 
编译输出为:
| 1 2 3 4 5 6
 | .content {     width: 60px; } .container {     width: 60px; }
 | 
在SASS中情况如下:
| 1 2 3 4 5 6 7 8
 | $size: 40px; .content {     width: $size; } $size: 60px; .container {     width: $size; }
 | 
编译输出为:
| 1 2 3 4 5 6
 | .content {     width: 40px; } .container {     width: 60px; }
 | 
Stylus和SASS行为相同,变量的计算以变量最近一次的定义为准。
变量插值
预处理器中定义的变量不仅可以用作属性值,还可以用作选择器,属性名等,这就是变量插值。
变量名插值
Less中支持以@@var的形式引用变量,即该变量的名字是由@var的值决定的。
选择器插值
以类选择器为例
LESS:
| 1 2 3 4
 | @way: new; .@{way}-task {     font-size: 18px; }
 | 
SASS:
| 1 2 3 4
 | $way: new; .#{$way}-task {     font-size: 18px; }
 | 
Stylus:
| 1 2 3
 | way: new; .{way}-task     font-size 18px
 | 
解析结果都是:
| 1 2 3
 | .new-task {     font-size: 18px; }
 | 
注意:在Less中,通过选择器插值生成的规则无法被继承。
@import插值
Sass中只能在使用url()表达式时进行变量@import插值:
| 1 2
 | $device: mobile; @import url(styles.#{$device}.css);
 | 
Less中可以在字符串中进行插值:
| 1 2
 | @device: mobile; @import "styles.@{device}.css";
 | 
Stylus中没有@import插值,但是可以利用其字符串拼接的功能实现:
| 1 2
 | device = "mobile" @import "styles." + device + ".css"
 | 
属性名插值
三个预处理器均支持属性名插值,使用方式且和上述插值类似。
嵌套
如果需要在相同的父元素中选择多个子元素,需要一遍又一遍地写父元素,如果用CSS预处理器就可以不用重复写父元素,并且父元素和子元素的关系一目了然。
三种预处理器的嵌套语法是一致的,引用父级选择器的标记&也相同。除了&,Sass和Stylus还分别用@at-root和”/“符号作为嵌套时根规则集的选择器引用。首先以LESS为例讨论嵌套语法:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
 | #sort {     margin-top: 24px;     ul {         margin-left: 8px;         line-height: 36px;         vertical-align: middle;         } } input {     width: 80px;     &:-ms-input-placeholder {     font-size: 16px;     color: @white;     } }
 | 
编译结果为:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
 | #sort {     margin-top: 24px; } #sort ul {     margin-left: 8px;     line-height: 36px;     vertical-align: middle; } input {     width: 80px; } input:-ms-input-placeholder {     font-size: 16px;     color: @white; }
 | 
SASS还提出了属性嵌套,属性嵌套指的是有些属性拥有相同的开始单词,如border-width,border-color都是以border开头。官网的实例如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13
 | .fakeshadow {     border: {         style: solid;         left: {             width: 4px;             color: #888;         }         right: {             width: 2px;             color: #ccc;         }     } }
 | 
生成的CSS为:
| 1 2 3 4 5 6 7
 | .fakeshadow {     border-style: solid;     border-left-width: 4px;     border-left-color: #888;     border-right-width: 2px;     border-right-color: #ccc; }
 | 
混入(mixin)
mixins有点像C语言中的宏,当某段CSS经常需要在多个元素中使用时,可以为这些共用的CSS定义一个mixin,然后只需要在需要引用这些CSS地方调用该mixin即可。
三种预处理器的mixin使用方式的差异比较大,下面分别说明。
LESS混入方式如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 | .my-mixin {     color: black; } .my-other-mixin() {     background: white; } .my-hover-mixin() {     &:hover {         border: 1px solid red;   } } .border-radius(@radius: 5px) {     -webkit-border-radius: @radius;         -moz-border-radius: @radius;             border-radius: @radius; } .class {     .my-mixin;     .my-other-mixin; } button {     .my-hover-mixin(); } #header {     .border-radius(4px); } .button {     .border-radius; }
 | 
编译输出为:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 | .my-mixin {     color: black; } .class {     color: black;     background: white; } button:hover {     border: 1px solid red; } #header {     -webkit-border-radius: 4px;         -moz-border-radius: 4px;             border-radius: 4px;     } .button {     -webkit-border-radius: 5px;         -moz-border-radius: 5px;             border-radius: 5px;     }
 | 
LESS的mixin需要注意的是同名的mixin不是后面的覆盖前面的,而是会累加输出。这就会产生一个问题,如果存在和mixin同名的class样式,并且mixin没有参数,则在调用时会把对应的class样式一起输出,这显然不是我们所需要的。
SASS的mixin用法如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
 | @mixin center-block {     margin-left:auto;     margin-right:auto; } .demo{     @include center-block; } @mixin horizontal-line($border:1px dashed #ccc, $padding:10px){     border-bottom:$border;     padding-top:$padding;     padding-bottom:$padding;   } .imgtext-h li{     @include horizontal-line(1px solid #ccc); }
 | 
编译结果为:
| 1 2 3 4 5 6 7 8 9
 | .demo{     margin-left:auto;     margin-right:auto; } .imgtext-h li {     border-bottom: 1px solid #cccccc;     padding-top: 10px;     padding-bottom: 10px; }
 | 
Sass用@mixin和@include两个指令清楚地说明了mixin的定义和引用方式。
Stylus的mixin和Sass的类似:
| 1 2 3 4 5 6
 | border-radius(n)     -webkit-border-radius n     -moz-border-radius n     border-radius n form input[type=button]     border-radius 5px
 | 
继承
继承其实和混入的作用差不多,那为什么还需要继承呢?混入确实很好用,但是如果多个地方都混入同样的代码,会造成代码的重复。例如:
| 1 2 3 4 5 6 7 8
 | .block {     margin: 10px 5px;     padding: 2px; } p {     .block;     border: 1px solid #EEE; }
 | 
会输出:
| 1 2 3 4 5 6 7 8 9
 | .block {     margin: 10px 5px;     padding: 2px; } p {     margin: 10px 5px;     padding: 2px;     border: 1px solid #EEE; }
 | 
而我们期望的输出实际是:
| 1 2 3 4 5 6 7 8
 | .block, p {     margin: 10px 5px;     padding: 2px; } p {     border: 1px solid #EEE; }
 | 
用继承就可以实现上面的输出,不会有重复的代码(以SASS为例):
| 1 2 3 4 5 6 7 8
 | .block {     margin: 10px 5px;     padding: 2px; } p {     @extend .block;     border: 1px solid #EEE; }
 | 
Stylus的继承来源于SASS,两者使用方式相同。而LESS则用伪类来实现继承:
| 1 2 3 4 5 6 7 8
 | .block {     margin: 10px 5px;     padding: 2px; } p {     &:extend(.block);     border: 1px solid #EEE; }
 | 
Less默认只继承父类本身的样式,如果要同时继承嵌套定义在父类作用域下的样式,得使用关键字all,比如&:extend(.block all)。
函数
三种预处理器都有自己的内置函数,例如颜色处理,类型判断等。LESS中不能自定义函数,SASS和Stylus可以。
SASS自定义函数用法如下,需要使用@function,并用@return指令返回结果:
| 1 2 3 4 5 6 7
 | @function pxToRem($px) {     @return $px / 2; } body{     font-size: pxToRem(32px); }
 | 
Stylus中则无需这些指令:
| 1 2 3 4
 | pxToRem(n)     n / 2 body     font-size: pxToRem(32px)
 | 
@import
@import的作用是从其他样式表导入样式,三种预处理器的@import的使用方式各不相同。
除了基本的功能外,LESS引入了import选项来扩展@import的语法。语法如下:
| 1
 | @import (keyword) "filename";
 | 
其中keyword可以是如下几种选项(可以联合使用)。
1.reference:使用一个外部文件参与编译,但不输出其内容。
2.inline:直接将引入的文件放入输出文件中,但不处理这个引入的文件。
3.less:不管文件扩展名是什么都将该文件作为一个LESS文件处理。
4.css:不管文件扩展名是什么都将该文件作为一个CSS文件处理。
5.once:只引入文件一次(去重),这是默认方式。
6.multiple:可以引入文件多次。
SASS则没有LESS的这些扩展语法,它自己推断引入的方式。它的@import 不会被去重,多次引入会导致一个样式文件被多次输出到编译结果中。
Stylus的@import和SASS一样都是自己推断引入的方式,但是Stylus可以进行引入文件的去重,它有一个自定义的指令@require,用法和@import一样,但引入的文件只会编译一次。
运算符
三种预处理器都具有运算的特性,可以对数值型的Value(如:数字、颜色、变量等)进行加减乘除四则运算。
Stylus的中文文档中,详细讨论了Stylus的运算符
逻辑控制
Sass中通过@if、@else 实现条件判断来提供语言的流控制,通过@for、@each、@while实现循环,然后配合map和list这两种数据类型可以实现多数编程语言提供的功能。
SASS中还实现了一个三目判断,语法为:if($condition, $if_true, $if_false) 。三个参数分别表示:条件,条件为真的值,条件为假的值。
Stylus中通过if、else if、else、unless(基本与if相反)实现条件判断来提供语言的流控制,通过for/in实现循环迭代。
而LESS中没有上述复杂的语法,只通过guarded mixins代替if/else实现简单的条件判断。举例如下:
| 1 2 3 4 5 6 7 8 9
 | .mixin (@a) when (lightness(@a) >= 50%) {     background-color: black; } .mixin (@a) when (lightness(@a) < 50%) {     background-color: white; } .mixin (@a) {     color: @a; }
 | 
以上就是三种CSS预处理器的主要区别,实际项目中使用哪种CSS预处理器还需要自己斟酌,多踩坑才能体会到哪种预处理器最适合你当前的项目。