CSS 预处理的区别的深度比较 - Stylus/Sass/Less

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 加了 !,其他都是一样的。也支持后添加的条件表达式,类似于 CoffeeScriptRuby。条件表达式不需要括号

Loop

  • Sass 的循环主要有两种语法 @for $var from <start> through <end>@for $var from <start> to <end> 区别在于 throughto 的含义:当使用 through 时,条件范围包含 <start><end> 的值,而使用 to 时条件范围只包含 <start> 的值不包含 <end> 的值。另外,$var 可以是任何变量,比如 $i<start><end> 必须是整数值。反正 Sass 的 for 循环只支持整数。
    也有 @while 循环,他的就简单多了,只需要接受一个条件表达式就可以了。
    但是如果你想循环其他的元素,那么你需要使用 @each $var in <list>支持解构
  • Less 首先大家要清楚,Less 中的变量都是懒加载的所,他无法实现循环中对变量的有效控制,所以他支持 Mixin 内调用自己,也即是说通过 GuardPattern 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 来处理。

其他功能 Others

Error Handle

  • Stylus
    • 支持自定义的错误输出,通过 error 函数

Introspection

  • Stylus
    • 允许 Mixin 和 Function 使用反射获取对应的信息

生态 Community

Stylus

  • nib 各种工具大集合,也自带各种 hack 类。

总结

不过总的来说,这种预处理器估计是因为太简单了吧,所以文档写的都很直接但是不深入,也没有类似于编程语言的 Spec,只是给你简单介绍了一下例子以及如何使用。
很蛋疼。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容