Yapei Li

专注于前端领域

0%

SCSS

Sass 和 SCSS 区别

SCSS Sass 3引入新的语法,其语法完全兼容 CSS3,并且继承了Sass的强大功能。

SassSCSS其实是同一种东西,我们平时都称之为 Sass,两者之间不同之处有以下两点:

  • 文件扩展名不同,Sass 是以“.sass”后缀为扩展名,而 SCSS 是以“.scss”后缀为扩展名
  • 语法书写方式不同Sass 是以严格的缩进式语法规则来书写,不带大括号({})和分号(;),而 SCSS 的语法书写和我们的 CSS 语法书写方式非常类似。

SASS版本3.0之前的后缀名为.sass,而版本3.0之后的后缀名.scss

Sass 支持两种不同的语法。两种语法可以互相加载,所以 选择哪一种语法取决于你和你的团队。

Sass 语法(缩进的语法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@mixin button-base()
@include typography(button)
@include ripple-surface
@include ripple-radius-bounded

display: inline-flex
position: relative
height: $button-height
border: none
vertical-align: middle

&:hover
cursor: pointer

&:disabled
color: $mdc-button-disabled-ink-color
cursor: default
pointer-events: none

SCSS 语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@mixin button-base() {
@include typography(button);
@include ripple-surface;
@include ripple-radius-bounded;

display: inline-flex;
position: relative;
height: $button-height;
border: none;
vertical-align: middle;

&:hover { cursor: pointer; }

&:disabled {
color: $mdc-button-disabled-ink-color;
cursor: default;
pointer-events: none;
}
}

SCSS 安装

首先安装css-loader、style-loader、node-sass、sass-loader

1
2
npm install css-loader style-loader --save-dev
npm install node-sass sass-loader --save-dev

webpack.config.js配置文件中添加对应的loader

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
const path = require("path");
const {VueLoaderPlugin} = require('vue-loader');
module.exports = {
entry: './webapp/App.js',
output: {
filename: 'App.js',
path: path.resolve(__dirname, './dist')
},
module: {
rules: [
{
test: /\.scss/,
use: ['style-loader', 'css-loader','sass-loader']
},
{
test: /\.vue$/,
use: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin()
],
mode: "production"
}

SCSS api

变量,以$开头

1
2
3
4
5
$border-color:#aaa; //声明变量
.container {
$border-width:1px;
border:$border-width solid $border-color; //在属性值中使用变量
}

$border-color在大括号之外称为全局变量,顾名思义任何地方都可以使用
$border-width是在.container之内声明的,是一个局部变量,只有.container内部才能使用

SCSS中变量名使用中划线或下划线都是指向同一变量的

$border-color$border_color 是同一个变量

(1)变量名使用中划线或下划线都是指向同一变量的。
(2)后定义的变量声明会被忽略,但赋值会被执行,这一点和ES5var声明变量是一样的。

1
2
3
4
5
6
7
8
9
10
$border-color:#aaa; //声明变量
$border_color:#ccc;
.container {
$border-width:1px;
border:$border-width solid $border-color; //使用变量
}
//编译后的CSS
.container {
border:1px solid #ccc; //使用变量
}

插值语法#{ }

在选择器和属性名中使用 Scss 的变量
使用 #{ } 意味着,靠近它的运算符都将被视为纯 CSS,这可以避免各种运算。

1
2
3
4
5
6
7
8
/* $变量 */
$name:button;
$new-border:border-radius;*斜体样式*

/* 插值表达式 */
div > #{ $name } {
#{ $new-border }:10px;
}

嵌套规则

css 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*css*/
.container ul {
border:1px solid #aaa;
list-style:none;
}

.container ul:after {
display:block;
content:"";
clear:both;
}

.container ul li {
float:left;
}

.container ul li>a {
display:inline-block;
padding:6px 12px;
}

scss 实现

嵌套选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*scss*/
.container ul {
border:1px solid #aaa;
list-style:none;

li {
float:left;
}

li>a {
display:inline-block;
padding:6px 12px;
}
}

.container ul:after {
display:block;
content:"";
clear:both;
}

嵌套中的父级选择器&

只能在嵌套内部使用父级选择器,否则SCSS找不到父级元素会直接报错。 在各种伪类选择器中,父级选择器是十分常用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*scss*/
.container ul {
border:1px solid #aaa;
list-style:none;

li {
float:left;
}

li>a {
display:inline-block;
padding:6px 12px;
}

&:after {
display:block;
content:"";
clear:both;
}
}

嵌套组合选择器

在嵌套规则中可以写任何css代码,包括群组选择器,),子代选择器>),同层相邻组合选择器+)、同层全体组合选择器~)等等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*scss*/
.container ul {
border:1px solid #aaa;
list-style:none;

li {
float:left;

>a {
display:inline-block;
padding:6px 12px;
}
}

&:after {
display:block;
content:"";
clear:both;
}
}

子代选择器可以写在外层选择器右边(如下述例子)也可以写在内层选择器左边(如上述例子)

写在外层选择器右边时要特别注意,他会作用于所有嵌套的选择器上,尽量不要采用这类写法

1
2
3
4
5
6
li >{ 
a {
display:inline-block;
padding:6px 12px;
}
}

嵌套属性

scss识别一个属性以分号结尾时则判断为一个属性,以大括号结尾时则判断为一个嵌套属性,规则是将外部的属性以及内部的属性通过中划线连接起来形成一个新的属性

css写法

1
2
3
4
5
6
/*css*/
li {
border:1px solid #aaa;
border-left:0;
border-right:0;
}

SCSS重写一遍

1
2
3
4
5
6
li {
border:1px solid #aaa {
left:0;
right:0;
}
}

导入SCSS文件

css提供了@import命令在css内部引入另一个css文件,浏览器只有在执行到@import语句后才会去加载对应的css文件,导致页面性能变差,故基本不使用

SCSS中的@import命令

导入变量的优先级问题-变量默认值

后导入的会覆盖前边的 变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*App1.scss*/
$border-color:#aaa; //声明变量
@import App2.scss; //引入另一个SCSS文件
.container {
border:1px solid $border-color; //使用变量
}
/*App2.scss*/
$border-color:#ccc; //声明变量

/*生成的css文件*/
.container {
border:1px solid #ccc; //使用变量
}

//样式被覆盖了

有时候我们希望引入的某些样式不更改原有的样式,这时我们可以在引入的css使用变量默认值

!default只能使用于变量中

1
2
3
4
5
6
7
8
9
10
11
12
13
/*App1.scss*/
$border-color:#aaa; //声明变量
@import App2.scss; //引入另一个SCSS文件
.container {
border:1px solid $border-color; //使用变量
}
/*App2.scss*/
$border-color:#ccc !default; //声明变量

/*生成的css文件*/
.container {
border:1px solid #aaa; //使用变量
}

导入的文件App2.scss只在文件中不存在$border-color时起作用,若App1.scss中已经存在了$border-color变量,则App2.scss中的$border-color不生效

嵌套导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*App1.scss*/
$border-color:#aaa; //声明变量
.container {
@import App2.scss; //引入另一个SCSS文件
border:1px solid $border-color; //使用变量
}
/*App2.scss*/
$border-color:#ccc !default; //声明变量
p {
margin:0;
}

/*生成的css文件*/
.container {
border:1px solid #aaa; //使用变量
}
.container p {
margin:0;
}

使用原生@import

SCSS也可以直接导入css文件

1
@import 'App.css';

注释

注释有两种:
(1)/*注释*/:这种注释会被保留到编译后的css文件中。

(2)//注释:这种注释不会被保留到编译后生成的css文件中。

混合器(函数)

声明一个函数@mixin

重复的代码片段都可以考虑使用混合器将他们提取出来复用

1
2
3
4
5
6
@mixin border-radius{
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
color:red;
}

混合器作用域内的属性都是return的值

可以为函数传参数
1
2
3
4
5
6
@mixin get-border-radius($border-radius,$color){
-moz-border-radius: $border-radius;
-webkit-border-radius: $border-radius;
border-radius: $border-radius;
color:$color;
}
设置混合器的默认值
1
2
3
4
5
6
@mixin get-border-radius($border-radius:5px,$color:red){
-moz-border-radius: $border-radius;
-webkit-border-radius: $border-radius;
border-radius: $border-radius;
color:$color;
}

使用函数@include

1
2
3
4
5
6
7
8
9
10
11
.container {
border:1px solid #aaa;
@include get-border-radius; //不传参则为默认值5px
@include get-border-radius(10px,blue); //传参
}
/*多个参数时,传参指定参数的名字,可以不用考虑传入的顺序*/
.container {
border:1px solid #aaa;
@include get-border-radius; //不传参则为默认值5px
@include get-border-radius($color:blue,$border-radius:10px); //传参
}

函数插槽@content

@content用在mixin里面的,当定义一个mixin后,并且设置了@content
@include的时候可以传入相应的内容到mixin里面

1
2
3
4
5
6
7
8
9
$color: white;
@mixin colors($color: blue) {
background-color: $color;
@content;
border-color: $color;
}
.colors {
@include colors { color: $color; }
}

编译后

1
2
3
4
5
.colors {
background-color: blue;
color: white;
border-color: blue;
}

继承

一个已经存在的css样式类,可以被其他样式类继承。

实现以下css

1
2
3
4
5
6
7
8
9
10
11
.btn, .btn--primary, .btn--info {
border: 1px solid blue;
}

.btn--primary {
color: black;
}

.btn--info {
color: gray;
}

scss实现

1
2
3
4
5
6
7
8
9
10
11
12
13
.btn {
border: 1px solid blue;
}

.btn--primary {
color: black;
@extend .btn;
}

.btn--info {
color: gray;
@extend .btn;
}

继承多个选择器

SCSS代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.one {
width:100px;height:100px;
}
.two {
/*继承的样式*/
@extend .one;
@extend .three;
/*继承的简便写法*/
@extend .one, .three;
/*独立的样式*/
background:red;
border:5px solid #000;
}
.three {
padding:10px;
}
编译后的CSS代码
1
2
3
4
5
6
7
8
9
10
11
12
13
.one, .two {
width: 100px;
height: 100px;
}

.two {
background: red;
border: 5px solid #000;
}

.three, .two {
padding: 10px;
}

链型继承

类名“.three”继承类名“.two”,而类名“.two”又继承了类名“.one

SCSS代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.one {
width:100px;height:100px;
}
.two {
/*继承的样式*/
@extend .one;
/*独立的样式*/
background:red;
border:5px solid #000;
}
.three {
/*继承的样式*/
@extend .two;
/*独立的样式*/
padding:10px;
}
编译后的CSS代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

.one, .two, .three {
/*继承的样式*/
width: 100px;
height: 100px;
}

.two, .three {
/*独立的样式*/
background: red;
border: 5px solid #000;
}

.three {
/*独立的样式*/
padding: 10px;
}

占位符

如果不被extend引用,它是不会被编译,也就是:不会占用css文件大小。这是和继承最大区别。

定义被继承的样式占位符 %

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
%btn {
border: 1px solid blue;
}

// 没有被extend
// 不会出现在css文件中
%test-btn {
border: 1px solid black;
}

.btn--primary {
color: black;
@extend %btn; //继承样式
}

.btn--info {
color: gray;
@extend %btn; //继承样式
}

编译后

1
2
3
4
5
6
7
8
9
10
11
.btn--primary, .btn--info {
border: 1px solid blue;
}

.btn--primary {
color: black;
}

.btn--info {
color: gray;
}

继承+占位符

1
2
3
4
5
6
7
8
9
10
%border-style {
border: 1px solid blue;
}
.container {
@extend %border-style; //继承占位符
color:red;
}
.container1 { //继承另一个选择器
@extend .container;
}

编译后

1
2
3
4
5
6
7
8
.container {
border: 1px solid blue;
color:red;
}
.container .container1 {
border: 1px solid blue;
color:red;
}

其他

scss 还支持 if for 等等

SCSS提供了标准的算术运算符,例如+、-、*、/、%

@at-root指令

可以将一个或多个样式规则生成在样式文件根层级上,而不是嵌套在其父选择器中:

1
2
3
4
.parent {
...
@at-root .child { ... }
}

编译后:

1
2
.parent { ... }
.child { ... }
1
2
3
4
5
6
7
8
.parent {
...
@at-root {
.child1 { ... }
.child2 { ... }
}
.step-child { ... }
}
1
2
3
4
.parent { ... }
.child1 { ... }
.child2 { ... }
.parent .step-child { ... }

SCSS内建了很多函数,包括数值运算、字符串操作、颜色操作、选择符操作等

参考文档:http://jimyuan.github.io/blog/2017/06/07/sass-preset-functions-learning.html

str-index($string, $substring)

返回一个下标,标示 $substring 在 $string 中的起始位置。没有找到的话,则返回 null 值
下标都是从 1 开始。

1
2
3
4
str-index(abcd, a)  => 1
str-index(abcd, ab) => 1
str-index(abcd, X) => null
str-index(abcd, c) => 3

覆盖全局变量 !global

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$color: blue;
a{
$color: red;
color: $color; //red
}
p{
color: $color; //blue
}

//但是,若使用 !global

span{
$color: yellow !global;
color: $color; //yellow
}
div{
color: $color; //yellow
}