CSS Preprocess Different
在前端界,有三大 CSS 预处理器,分别是 SASS(SCSS)
, Less
, Stylus
。
或许有的人在问,那么 PostCSS
算什么呢?其实他只能算是 CSS 解析器。不能算作预处理器。
由于这三大预处理出现的时间不同,而他们之间也会有一些不同的地方,所以在这里我们整理一下他们那些地方不同,方便学习记忆以及选择。当然,我们这里比较的时候会深入比较,而不是泛泛而谈。
但是前提最好是你有对其中一种预处理的使用经验,可以通过我这篇文章可以快速了解其他的预处理的语法格式等等内容。如果你没有相关的语言语法,那么请你学习其中的一个之后再来阅读此文章。
如果你问我现在用什么,当然是 stylus 了,:>
语法 Syntax
Sass(Scss)
Sass 有两种语法格式。首先是 SCSS (Sassy CSS), 这种格式仅在 CSS3 语法的基础上进行拓展,所有 CSS3 语法在 SCSS 中都是通用的,同时加入 Sass 的特色功能。此外,SCSS 也支持大多数 CSS hacks 写法以及浏览器前缀写法 (vendor-specific syntax),以及早期的 IE 滤镜写法。这种格式以 .scss 作为拓展名。
另一种也是最早的 Sass 语法格式,被称为缩进格式 (Indented Sass) 通常简称 "Sass",是一种简化格式。它使用 “缩进” 代替 “花括号” 表示属性属于某个选择器,用 “换行” 代替 “分号” 分隔属性,很多人认为这样做比 SCSS 更容易阅读,书写也更快速。缩进格式也可以使用 Sass 的全部功能,只是与 SCSS 相比个别地方采取了不同的表达方式。
Less
Less 其实在某种程度上很像 Scss,它和 CSS 也是完全兼容的。要有分号,要有冒号,还有大括号。
Stylus
他的语法和 Sass 有一点点类似,但是最大的特点便是没有分号,没有冒号,没有大括号,通过缩进和换行。一切的定义方式都是按照编程的风格定义的,像编程一样的函数调用,像编程一样的变量定义,像编程一样的书写方式。
除此之外,他还有一个最大的优点便是他随意的格式,官方推荐的是无分号,无冒号,无大括号,但是他可以有分号,可以有冒号,也可以有大括号,也就是说你愿意的话,你完全可以当做普通的 CSS 文件来写,甚至是 Less,Sass 等其他预处理器的风格。所以这也就是我为什么用这个的原因。
注释
- Sass (
//
,/* */
),多行注释会完整的输出到编译之后的文件,但是单行不会,所以如果是对文件描述的可以使用多行注释,如果只是开发相关的,请使用单行注释。当然这些行为参数可以控制。
可以使用!
作为多行注释的第一个字符,表示即使在压缩模式下也保留这条注释并输出到 CSS 文件中,通常用于版权的添加。
Playground
具体内容请查看官方文档。
变量 Variables
开头标识
- Sass 用
$
表示变量 - Less 用
@
表示变量 - Stylus 没有特殊的开头标识
赋值方式
- Sass/Less 通过
var-name: var-value;
的方式 - Stylus 通过
=
赋值的方式,类似于编程语言的编程方式
Sass
Sass 的变量以 $
开头,赋值方法与 CSS 属性的写法一致:
$var-name: var-value;
直接使用就可以获取变量的值,非常简单。同样支持块级作用域,也分为全局变量(所有 Rule 外面定义的变量)和局部变量(在规则内部定义的变量)。同时支持将局部变量声明为全局变量,通过 !global
声明:
#main {
$width: 5em !global;
width: $width;
}
#sidebar {
width: $width;
}
// output
#main {
width: 5em;
}
#sidebar {
width: 5em;
}
Sass 支持 6 种主要的数据类型
- 数字,
1
,2
,13
,10px
- 字符串,有引号字符串与无引号字符串,
"foo"
,'bar'
,baz
- 颜色,
blue, #04a3f9, rgba(255,0,0,0.5)
- 布尔型,
true
,false
- 空值,
null
- 数组 (list),用空格或逗号作分隔符,多维数组用
()
包括,逗号比空格的优先级要高,所以会优先用逗号分隔数组,1.5em 1em 0 2em
,Helvetica, Arial, sans-serif
。输出时扁平化输出。 - maps, 相当于 JavaScript 的 object,
(key1: value1, key2: value2)
Less
Less 的变量是以 @
开头,语法格式类似于 CSS 的 Property 的书写格式,与 Sass/Scss 有一些类似:
@var-name: var-value;
使用的时候也是带着 @
,他支持变量插值,类似于 PHP 那样的。比如说
@red: 1;
@var1: red;
@var2: @{@var1}; // 1
// or
@var2: @@var1;
同时变量的执行都是懒加载,也就是说变量的值得计算都是在你用到的时候计算的,例如
@a: 1;
@b: @a;
.a {
content: @b; // 1
}
.b {
@a: 2;
content: @b; // 2
}
当然他也有作用域,有根级的作用域,也有块级的作用域。所以我们只需要在当前作用于修改变量,并不会影响上级作用域的变量的内容。
但是文档中并没有提及类型的问题,所以不得而知。
Stylus
变量通过 =
进行定义,就像是普通的编程的变量一般,不需要什么特殊的前缀:
var-name = var-value
不过我还是推荐使用 $
作为开头,因为当 stylus 写大的时候,你会发现经常你会把变量和属性值弄混,所以建议用 $
开头,这样我们可以很清晰的看出来,那个是变量,那个是属性值。
运算 Operations
算数运算
所有的算数运算都支持单位的运算,也支持自动转化,比如说 20mm + 4in
= 121.6mm
。如果发现单位无法转换,那么一般是忽略单位只有单独运算,运算结束之后再加上左操作数的单位,例如 5s - 3px
= 2s
。
有一点例外的就是,对于乘法除法来说,单位相乘是没有什么意义的,所以在乘除当中和单位无法转换的情况相同。
如果操作的类型是颜色类型的话,那么运算都是相对于 R/G/B 单独计算的,也就是说 #0000ff + #000001
= #0000ff
而不是 #000100
,不存在进位的情况。
数组运算
- Sass 不支持任何数组的运算符,全部都是通过函数操控
- Less 没有找到文档,测试无法使用
[]
,目测也是通过和 Sass 一样的方式。 - Stylus 支持
[]
下标取值
函数 Function
函数定义
- Sass,通过一个 @function 来定义一个函数,然后通过 @return 返回一个值
- Less,准确说没有函数这个概念,但是他有 混入(Mixin) 来充当函数的概念。不过这个也是合理的,毕竟 CSS 是最后需要输出一段字符串,一切操作都是为了生成字符串,所以没有函数也是完全可以接受的。
嵌套 Nest rules
所有的预处理中都有嵌套的功能,而且语法都是类似的,将 Rule 写在某个 Rule 里面,便可以实现嵌套的功能。
不需要每次都输入父选择器了,方便快捷高效。
同理,也都有 &
父选择引用,可以方便的在 Rule 内部定义父级的 Rule,一般用来定义 hover
或者相连的选择器 &.other-class
。
Less 中还支持嵌套 Directive,比如 media, keyframe 等等。这些 Directives 及时再非常深的地方,也会一起冒泡到顶级。
混合 Mixins
定义
- Sass 会复杂一些,需要使用
@mixin
指令定义。简写为=
- Less 很简单,一个类就可以是一个混入的定义,当然 ID 也可以,不过标签不可以,也就是说你必须要有
.
或者#
开头。在这种情况下,所有的混入自然会输出的(它也没法区分混入和普通类),如果你不想让他输出,那么可以在定义的类名后面添加()
从而不让其在最后的结果中输出,感觉就像是定义了一个函数 :) - Stylus 很简单,在后面添加
()
即可,类似于普通的函数。
使用
- Sass 需要通过
@include
指令引入混入。简写为+
- Less 通过直接引用类名可以直接进行混入,当然加不加括号都是可以的
.a()
或者.a
都是正确的写法。 - Stylus 使用 Mixin 主要有两种方式,第一种就是类似于函数调用的方式,通过
()
,第二种便是将 Mixin 当做普通的属性来使用(正是因为这个的存在,所以你可以定义一个和标准 CSS 属性同名的 Mixin,从而修改这个属性的输出,通常用于 hack)。推荐使用第二种方式,可以保持整个代码的整洁。
参数、关键词参数、 Rest 参数(...
)
这三者都支持,定义方式也是大同小异,没什么可以说的。也都支持类似于 JS 中 arguments
的变量。
- Sass 不支持,用
,
分隔参数 - Less
@arguments
, 用;
分隔参数,这样更像是属性定义。 - Stylue
arguments
,用,
分隔参数
不过 Less 有一点比较特殊,因为他没有函数,也就是说他很难做到根据不同的输入有不同的输出(属性上的不同),所以它支持模式匹配,也就是说根据某些参数来运行不同的 Mixin,其实就是 Mixin 重载。
向 Mixin 传入自定义 Rules
- Sass 支持像 Ruby 的 Block,也就是说你可以直接传入一段自定义的 Block,通过
@content
引用这段 Block,例子请查看官网。 - Less 目前没有发现这个功能。
- Stylus 在使用 Mixins 前面添加
+
就可以在后面添加 Block。其实只是想和 Mixin 定义区分开。
Mixin 定义嵌套
目前在文档中都没有相关内容的介绍,发现部分是可以的。
- Sass 不允许嵌套定义
- Less 可以定义,而且可以从外面引用到
- Stylus 可以定义,但是限制作用域,只能在定义的位置使用。
总体来说,Sass 和 Less 在混入方面我还是喜欢 Less,简单直接。
控制语句 Controls
if
- Sass if 有两种,第一种是
if()
函数,第二种便是@if
指令。如果指令的表达式为真,那么久输出值,否则就不输出。当然也有@else if
和@else
。条件表达式不需要括号 - Less 没有直接对应的东西,但是他有一个
Guard
的概念,可以认为是变种的if
。通过在某些选择器、Mixin 定义后面添加when (condition)
。只有当condition
为真的时候,相应的选择器、Mixin 才会起作用。 - Stylus
if/else
跟普通编程语言几乎是一模一样的,也可以嵌套,这里就不多讲了。不过 Stylus 还有一个unless
,就是 if 加了 !,其他都是一样的。也支持后添加的条件表达式,类似于CoffeeScript
和Ruby
。条件表达式不需要括号
Loop
- Sass 的循环主要有两种语法
@for $var from <start> through <end>
和@for $var from <start> to <end>
区别在于through
与to
的含义:当使用 through 时,条件范围包含<start>
与<end>
的值,而使用to
时条件范围只包含<start>
的值不包含<end>
的值。另外,$var
可以是任何变量,比如$i
;<start>
和<end>
必须是整数值。反正 Sass 的 for 循环只支持整数。
也有@while
循环,他的就简单多了,只需要接受一个条件表达式就可以了。
但是如果你想循环其他的元素,那么你需要使用@each $var in <list>
。支持解构 - Less 首先大家要清楚,Less 中的变量都是懒加载的所,他无法实现循环中对变量的有效控制,所以他支持 Mixin 内调用自己,也即是说通过
Guard
和Pattern Matching
通过递归实现循环。 - Stylus 简单来说就一个
for <val-name> [, <index-name>] in <expression>
。就跟普通的 JS 语句一样。和if
语句类似,他也支持后缀表达式。但是如果express
是 Hashes,那么表达式相应的变为for <key-name>, <value-name> in <hashes>
导入 Imports
无一例外,三大框架都使用了 @import
作为导入的关键字。这里就会有一个问题,因为 @import
在 CSS 中也有,于是就存在到底是将文件导入进来还是仅仅作为一个 CSS 的关键字输出呢?
总体来说有如下的规则:
- 如果是以
.css
结尾,那么直接输出,不导入 - 如果包含
url()
也直接输出到文件,不导入 - 如果包含
http://
或者https://
也就是网络请求,那么也直接输出到文件中,不导入 - 其他情况,当做普通的编程文件导入。
特殊情况
- Sass
- 如果包含 media queries 也直接输出,不导入。
- Less
- 如果包含
url()
或者http://
/https://
,并不直接输出,而是导入。 - Less 只有一种情况会直接输出,那么就是如果文件以
.css
结尾
- 如果包含
- Stylus
- 支持 glob 导入,支持 index 导入,类似于 node 的包管理机制一般。
同时这三大框架都支持 嵌套导入
。部分支持 引用导入
和 内联导入
,同时 Less
的 @import
默认是只导入一次的。
- Sass
- 支持引用导入,可以仅仅导入但是不输出为 CSS,只需要在文件名前添加下划线,但是导入语句中不需要
- 如果同时存在添加下换线与未添加下换线的同名文件时,添加下划线的会被忽略
- Less
- 支持
@import (options) "filename"
设置不同的导入模式,现在总共有 7 种,可以通过逗号分隔多个参数。- reference 引用导入,不输出 css
- inline 直接内联,输出为 css
- less 将文件当做 less 文件,忽略他的后缀名
- css 将文件当做 css 文件,忽略他的后缀名
- once 只导入一次(默认行为)
- multiple 导入多次
- optional 到文件未找到的时候,忽略这个错误,继续执行
- 支持
- Stylus
- 默认情况下
@import
是可以导入多次的,但是如果你想要导入一次的话,可以通过@require
,这两者只有导入次数的区别,没有别的区别。类似于 php 的 require 和 import。 - Stylus 是没有是否
引用导入
的这个功能的,因为 Stylus 不能将 Rule 作为 mixin 进行使用,所以就不会出现 Sass/Less 这种有一些 Rule 只是 mixin 而不是普通的 Rule 的情况,所以也就不需要有引用导入
这个功能。
- 默认情况下
扩展 Extends
简单说一些 Extend 和 Mixin 的区别的吧,Mixin 就像是 copy/paste,每次执行 Mixin ,都会在不同的 Rule 下面有相同的内容,他们不会进行复用。但是 Extend 会进行复用,将相同的内容提取出来,复用成一个 Rule。
不过有一点很特殊的是, Less 竟然没有 Extned。而且 Stylus 的 Extend 还是由 Sass 扩展过来的,所以他们两个基本相同,都使用 @extend
关键字。
而且 Sass 的 Extend 没有什么太多的概念,我就一起来讲了。
首先两者有一个最大的区别就是 Stylus
支持 nested selector,而 Sass
是不支持的。
其次两者都有 placeholder ,也就是仅仅用于 extend 的类,但是两者的定义方式不一样。
- Sass 通过将
#
/.
替换为%
- Stylus 通过在最前面添加
$
来实现的,其实用这个也能弥补 Stylus 没有那种只定义不输出的功能,只不过只能用于 Extend
也支持 optional
功能,其实就是如果找不到 Extend 类但是不报错的样子。通过在类后面添加 !optional
。
DEMO: @extend .a !optional, .b !optional
其他指令 Other Directives
@css
- Stylus
- 有的时候 Stylus 无法正确的处理 css 的选择器,你可以将 css 属性放入到
@css
标签内。内部的类都将作为普通的 css 来处理。
- 有的时候 Stylus 无法正确的处理 css 的选择器,你可以将 css 属性放入到
其他功能 Others
Error Handle
- Stylus
- 支持自定义的错误输出,通过
error
函数
- 支持自定义的错误输出,通过
Introspection
- Stylus
- 允许 Mixin 和 Function 使用反射获取对应的信息
生态 Community
Stylus
- nib 各种工具大集合,也自带各种 hack 类。
总结
不过总的来说,这种预处理器估计是因为太简单了吧,所以文档写的都很直接但是不深入,也没有类似于编程语言的 Spec,只是给你简单介绍了一下例子以及如何使用。
很蛋疼。。。