Sassv3.5文档一览

Posted by Yeoman on 2018-04-04

背景

因为nek-ui 2.0 要完全重构掉CSS部分,因此决定重新调研一下现在比较好的CSS预处理方案。参考了一下element-ui,发现element-ui在新版本已经从postcss+postcss-salad的方案迁移到了node-sass+sass-loader的方案。看了一些Sass的写法,发现有些语法已经看不懂了,于是决定系统性地过下Sass官方文档,学习下Sass的一些新特性。本文旨在翻译和整理学习(所有的样例都用Sass-cli验证过)。

为什么要用Sass

  1. Sass对于原生CSS是完全兼容的
  2. Sass在语法上扩展了变量(关于这一点,CSS规范也已经提供了解决方案),嵌套,还有混合(代码块)。
  3. 有很多现成的函数可以使用,比如mix($color1, $color2, [$weight])用来将两种颜色混合等等。
  4. 可以基本的流程控制,比如p { @if 1 + 1 == 2 { border: 1px solid; } }

很显然,上述的大部分特性在CSS规范中都没有被支持甚至是提出,因此我们需要CSS预处理器来帮助更方便地写CSS。

CSS扩展

Sass的CSS扩展上,除了支持嵌套语法,还有一些很有用的扩展:

1. 支持&来获得父级选择器

1
2
3
a {
&:hover { text-decoration: underline; }
}

2. 支持属性的嵌套写法(这个感觉没啥用- -

1
2
3
4
5
6
7
.funky {
font: {
family: fantasy;
size: 30em;
weight: bold;
}
}

3. 支持Placeholder Selectors

这个属性是配合@extend来使用的,

1
2
3
4
5
6
7
.btn {
color: #333;
}
.primary-btn {
@extend .btn;
}

这段CSS会被编译成:

1
2
3
.btn .primary-btn {
color: #333;
}

而如果用了Placeholder Selectors,看下区别:

1
2
3
4
5
6
7
%btn {
color: #333;
}
.primary-btn {
@extend %btn;
}

这段CSS会被编译成:

1
2
3
.primary-btn {
color: #333;
}

这样这个基础.btn样式则不会被渲染。

Sass注释

Sass支持/**///这两种形式的注释方式,前者在编译后会被保留,后者会被删除。

SassScript

1. 通过$关键字来定义变量

!global关键字可以让变量变成全局变量

2. 数据类型

  • Sass支持的数据类型
    • numbers
    • strings
    • colors
    • booleans
    • nulls
    • lists
    • maps
    • function refrences

这里重点记录一下number类型的运算(因为最常用:

  1. Sass支持+, - ,*,/和%的运算
  2. Sass在运算过程中保留单位,两个带有单位的值相乘会抛出异常(10px 10px),如果要使用乘法应该是`10px 10`
  3. /会被解析成除法的场景如下:
1
2
3
4
5
6
7
8
9
p {
font: 10px/8px; // 原生的CSS,不作为除法
$width: 1000px;
width: $width/2; // 使用了变量, 作为除法
width: round(1.5)/2; // 使用了函数, 作为除法
height: (500px/2); // 使用了括号, 作为除法
margin-left: 5px + 8px/2px; // 使用了 +, 作为除法
font: (italic bold 10px/8px); // 在一个列表(list)中,括号可以被忽略。
}
  1. 在使用-的时候主要注意两点:
    • 如果要作为减法使用,应该在-前后都留空格。
    • 如果要作为负数使用,要在前面留一个空格,后面不留

3. Interpolation#{}插值

前面提到Sass中可以用$关键字来定义变量,而插值的作用则是在选择器和属性名中使用变量。

1
2
3
4
5
$name: foo;
$attr: border;
p.#{$name} {
#{$attr}-color: blue;
}

这段CSS会被编译成:

1
2
p.foo {
border-color: blue; }

4. & in SassScript

前面提到过&可以直接作为父级选择器,那么如果把&用在SassScript中,常用的场景如下:

1
2
3
@mixin e($element) {
$selector: &;
}

5. !default(变量默认)

这个属性很简单,就是当一个变量已经被赋值过了,则不会被有!default修改的同名变量覆盖。

1
2
3
4
$content: "First content";
$content: "Second content?" !default;
// $content还是"First content"

@规则和指令

@import

Sass对于@import的扩展主要有两个功能:

  1. 支持导入.scss和.sass文件
  2. 被导入文件中所定义的变量或mixins都可以在主文件中被使用。

@extend

@extend的用法在上述例子中已经展示了,主要的作用就是复用CSS代码片段。

说到复用CSS,肯定会想到另一个功能@mixin,关于这两者的区别这篇译文写的比较好。结论是大部分场景下用mixin会更合适一些,所以就不去细看@extend的各种细致用法了。

@at-root

这个关键字类似CSS Module中的global关键字。用于将样式应用在文档的根结点, 而不是被嵌套在父容器下。

1
2
3
4
5
6
.parent {
color: #000;
@at-root .child {
color: #fff;
}
}

这段样式会被编译成:

1
2
3
4
5
6
.parent {
color: #000;
}
.child {
color: #fff;
}

默认情况下, @at-root只是移动到选择器的最外层,如果要移动到嵌套选择器(比如@media)的外层则需要使用@at-root (without: …) 和 @at-root (with: …)

Control Directives & Expressions

1. if()

if()和三目运算符? :很像:

1
2
3
4
.if {
width: if(true, 1px, 2px);
height: if(false, 1px, 2px);
}

会被编译成:

1
2
3
4
.if {
width: 1px;
height: 2px;
}

@if,@else if 和 @else

这个控制就和JS中的流程控制是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$type: monster;
p {
@if $type==ocean {
color: blue;
}
@else if $type==matador {
color: red;
}
@else if $type==monster {
color: green;
}
@else {
color: black;
}
}

会被编译成:

1
2
3
p {
color: green;
}

@for

for可以循环生成一组样式:

1
2
3
@for $i from 1 through 3 {
.item-#{$i} { width: 2em * $i; }
}

会被编译成:

1
2
3
4
5
6
7
8
9
10
11
.item-1 {
width: 2em;
}
.item-2 {
width: 4em;
}
.item-3 {
width: 6em;
}

@for $i from to 的写法输出是不包含

@each

@each的功能和@for比较像,@for的循环只限于数字递增递减,而@each可以自定义list,用@each来实现上面@for的需求,输出是一模一样的。

1
2
3
@each $i in 1, 2, 3 {
.item-#{$i} { width: 2em * $i; }
}

@each可以使用@each $var1,$var2, … in 这样的形式进行多重赋值

Mixin

Sass官方把Mixin单独拿作为一个章节来讲,可见Mixin在Sass中确实是一个很强大很实用的功能。

Mixin的基础用法和@extend没有二异,但是Mixin强大的地方在于支持参数传递,这样用法上就很像函数了:

1
2
3
4
5
6
7
8
@mixin silly-links($color) {
a {
color: $color;
background-color: red;
}
}
@include silly-links(blue);

编译成:

1
2
3
4
a {
color: blue;
background-color: red;
}

在参数传递的时候,还支持关键字参数,这样可读性会更强:

1
2
3
4
5
6
7
8
@mixin silly-links($color) {
a {
color: $color;
background-color: red;
}
}
@include silly-links($color: blue);

以及不定参数:

@mixin box-shadow($shadows…) {
box-shadow: $shadows;
}

1
2
3
.shadows {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}

会被编译成:

1
2
3
.shadows {
box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
}

还有一个很重要的功能,就是Mixin还可以配合@content支持直接传递一个代码块,这个功能对于实现Sass中的BEM很重要,关于BEM在Sass中的mixin实践,可以看这篇文章

1
2
3
4
5
6
7
8
9
@mixin b($block) {
.#{'kl-' + $block} {
@content;
}
}
@include b($block: header) {
color: #333;
}

会被编译成:

1
2
3
.kl-header {
color: #333;
}

函数指令

1
2
3
4
5
6
7
8
9
$grid-width: 40px;
$gutter-width: 10px;
@function grid-width($n) {
@return $n * $grid-width+($n - 1) * $gutter-width;
}
#sidebar {
width: grid-width(5);
}

会被编译成:

1
2
3
#sidebar {
width: 240px;
}

整体来说,Sass拥有变量,流程控制,Mixin,Function等等功能之后,都是为了让CSS能够具备一些简易的脚本功能,如果是这样的话,会不会React中CSS in JS的做法才是CSS的趋势走向?