背景
因为nek-ui 2.0 要完全重构掉CSS部分,因此决定重新调研一下现在比较好的CSS预处理方案。参考了一下element-ui,发现element-ui在新版本已经从postcss+postcss-salad的方案迁移到了node-sass+sass-loader的方案。看了一些Sass的写法,发现有些语法已经看不懂了,于是决定系统性地过下Sass官方文档,学习下Sass的一些新特性。本文旨在翻译和整理学习(所有的样例都用Sass-cli验证过)。
为什么要用Sass
- Sass对于原生CSS是完全兼容的
- Sass在语法上扩展了变量(关于这一点,CSS规范也已经提供了解决方案),嵌套,还有混合(代码块)。
- 有很多现成的函数可以使用,比如
mix($color1, $color2, [$weight])
用来将两种颜色混合等等。 - 可以基本的流程控制,比如
p { @if 1 + 1 == 2 { border: 1px solid; } }
很显然,上述的大部分特性在CSS规范中都没有被支持甚至是提出,因此我们需要CSS预处理器来帮助更方便地写CSS。
CSS扩展
Sass的CSS扩展上,除了支持嵌套语法,还有一些很有用的扩展:
1. 支持&来获得父级选择器
1 | a { |
2. 支持属性的嵌套写法(这个感觉没啥用- -
1 | .funky { |
3. 支持Placeholder Selectors
这个属性是配合@extend来使用的,
1 | .btn { |
这段CSS会被编译成:
1 | .btn .primary-btn { |
而如果用了Placeholder Selectors,看下区别:
1 | %btn { |
这段CSS会被编译成:
1 | .primary-btn { |
这样这个基础.btn样式则不会被渲染。
Sass注释
Sass支持/**/
和//
这两种形式的注释方式,前者在编译后会被保留,后者会被删除。
SassScript
1. 通过$关键字来定义变量
!global关键字可以让变量变成全局变量
2. 数据类型
- Sass支持的数据类型
- numbers
- strings
- colors
- booleans
- nulls
- lists
- maps
- function refrences
这里重点记录一下number类型的运算(因为最常用:
- Sass支持+, - ,*,/和%的运算
- Sass在运算过程中保留单位,两个带有单位的值相乘会抛出异常(10px 10px),如果要使用乘法应该是`10px 10`
/
会被解析成除法的场景如下:
1 | p { |
- 在使用
-
的时候主要注意两点:- 如果要作为减法使用,应该在
-
前后都留空格。 - 如果要作为负数使用,要在前面留一个空格,后面不留
- 如果要作为减法使用,应该在
3. Interpolation#{}
插值
前面提到Sass中可以用$关键字来定义变量,而插值的作用则是在选择器和属性名中使用变量。
1 | $name: foo; |
这段CSS会被编译成:
1 | p.foo { |
4. & in SassScript
前面提到过&可以直接作为父级选择器,那么如果把&用在SassScript中,常用的场景如下:
1 | @mixin e($element) { |
5. !default(变量默认)
这个属性很简单,就是当一个变量已经被赋值过了,则不会被有!default修改的同名变量覆盖。
1 | $content: "First content"; |
@规则和指令
@import
Sass对于@import的扩展主要有两个功能:
- 支持导入.scss和.sass文件
- 被导入文件中所定义的变量或mixins都可以在主文件中被使用。
@extend
@extend
的用法在上述例子中已经展示了,主要的作用就是复用CSS代码片段。
说到复用CSS,肯定会想到另一个功能@mixin
,关于这两者的区别这篇译文写的比较好。结论是大部分场景下用mixin会更合适一些,所以就不去细看@extend
的各种细致用法了。
@at-root
这个关键字类似CSS Module中的global关键字。用于将样式应用在文档的根结点, 而不是被嵌套在父容器下。
1 | .parent { |
这段样式会被编译成:
1 | .parent { |
默认情况下, @at-root只是移动到选择器的最外层,如果要移动到嵌套选择器(比如@media)的外层则需要使用@at-root (without: …) 和 @at-root (with: …)
Control Directives & Expressions
1. if()
if()
和三目运算符? :
很像:
1 | .if { |
会被编译成:
1 | .if { |
@if,@else if 和 @else
这个控制就和JS中的流程控制是一样的:
1 | $type: monster; |
会被编译成:
1 | p { |
@for
for可以循环生成一组样式:
1 | @for $i from 1 through 3 { |
会被编译成:
1 | .item-1 { |
@for $i from
to 的写法输出是不包含 的
@each
@each的功能和@for比较像,@for的循环只限于数字递增递减,而@each可以自定义list,用@each来实现上面@for的需求,输出是一模一样的。
1 | @each $i in 1, 2, 3 { |
@each可以使用@each $var1,$var2, … in
这样的形式进行多重赋值
Mixin
Sass官方把Mixin单独拿作为一个章节来讲,可见Mixin在Sass中确实是一个很强大很实用的功能。
Mixin的基础用法和@extend没有二异,但是Mixin强大的地方在于支持参数传递,这样用法上就很像函数了:
1 | @mixin silly-links($color) { |
编译成:
1 | a { |
在参数传递的时候,还支持关键字参数,这样可读性会更强:
1 | @mixin silly-links($color) { |
以及不定参数:
@mixin box-shadow($shadows…) {
box-shadow: $shadows;
}
1 | .shadows { |
会被编译成:
1 | .shadows { |
还有一个很重要的功能,就是Mixin还可以配合@content支持直接传递一个代码块,这个功能对于实现Sass中的BEM很重要,关于BEM在Sass中的mixin实践,可以看这篇文章:
1 | @mixin b($block) { |
会被编译成:
1 | .kl-header { |
函数指令
1 | $grid-width: 40px; |
会被编译成:
1 | #sidebar { |
整体来说,Sass拥有变量,流程控制,Mixin,Function等等功能之后,都是为了让CSS能够具备一些简易的脚本功能,如果是这样的话,会不会React中CSS in JS的做法才是CSS的趋势走向?