Sass语法学习(超详细)

1. 变量

1.1 变量声明与使用

sass中使用$符号来标识变量。

如,声明值为 #F90 的变量 $color

$color: #F90;

body {   
    color:$color;
}

输出结果为:

body {
    color: #F90;
}

1.2 变量的作用域

1.2.1 !global 标识符

Sass 允许使用 !global 标识符来设置局部变量为全局。

如:

​
$main-color: red;
h1 {
  $main-color: green!global;
  color:$main-color;
}
h2 {
  color:$main-color;
}

​

转换为 CSS 是这样的:

h1 {
  color: green;
}

h2 {
  color: green;
}

1.2.2  !default标识符

一般来说我们反复的声明一个重名变量,那么最后一个声明的变量值会覆盖上面所有的,比如像下面这样:

$main-color: red;
$main-color: green;
h1 {
  color:$main-color;
}

那么最后编译的时候会使用最后一次声明的变量值,也就是 green ,我们看下编译后的代码:

​h1 {
  color: green;
}

 !default 标识符,顾名思义,就是默认值,如果这个变量被声明并赋值了,那么就使用声明的值,否则就使用默认值。

$main-color: red; // 假如这个是其他开发者自己声明的
$main-color: green!default; // 假如这个是你的代码片段声明的
h1 {
  color:$main-color;
}

那么在最后编译的时候会使用 red 这个变量值,如果其他开发者没有声明这个变量,就会使用 green 这个变量值,我们来看下编译后的效果:

h1 {
  color: red;
}

2.嵌套

2.1 什么是嵌套?

在一般编写 CSS 的时候呢,我们一遍一遍的编写相同的选择器去处理深层级的样式,而 Sass 给你一种轻松的方式,你可以在一个样式规则中直接编写另一个样式规则,而不是重复相同的选择器,Sass 将自动组合内外部的选择器。

通俗点说就是:你可以在父选择器的样式中直接编写子元素的样式,同理你可以在一个子元素的样式中再去编写孙元素的样式,可以一层一层的嵌套着去写样式。

2.2 样式的嵌套

2.2.1 基本使用

父选择器里可以嵌套子选择器

如:有以下标签

 <div>
        <ul>
            <li></li>
        </ul>     
    </div>

可以直接这样写:

div{
    height: 100px;
    ul{
        height: 80px;
        li{
           height: 50px;
        }
    }
}

相当于:

div {
  height: 100px;
}
div ul {
  height: 80px;
}
div ul li {
  height: 50px;
}

scss嵌套打开(解析)的规则是:从外层到内层,将嵌套规则块打开,父级的选择器放在子级选择的前面组成一个新的选择器,然后再循环打开内部的嵌套块处理。

2.2.2 嵌套选择器列表

css中,使用,分割的群组选择器可以同时应用样式在多个选择器上,如:

h1, h2 {
  margin: 0;
}

sass的嵌套特性,在解开一个内嵌的群组选择器时,会把每一个内嵌选择器正确的结合起来:

.container{
  h1,h2,h3{
    margin-bottom:.8em;
  }
}

sass会组合成 .container h1、 .container h2.container h3 三者的群组选择器:.container h1, .container h2, .container h3{ xxx }

同样,群组选择器内的嵌套也会以这种方式解析:

nav, aside {
  a {color: blue}
}

// nav a, aside a {color: blue}

2.2.3 嵌套组合符选择器 

这三种选择器必须和其他选择器配合使用。

/* 子组合选择器> */
article > section { border: 1px solid #ccc }

/* 相邻组合选择器+  选择 元素后紧跟的指定元素 */
header + p { font-size: 1.1em }

/* 同层全体组合选择器~,选择所有跟在article后的同层article元素 */
article ~ article { border-top: 1px dashed #ccc }

在sass中使用时,可以通过嵌套直接生成正确的结果(位于外层选择器的后面,或内层选择器的前面均可!),而不需要使用&

article {
  /* 放在 里层选择器前边 */
  ~ article { border-top: 1px dashed #ccc }
  > section { background: #eee }
  /* 放在 外层选择器后边 */
  dl > {
    dt { color: #333 }
    dd { color: #555 }
  }
  nav + & { margin-top: 0 }
}

解开后的css为:

article ~ article { border-top: 1px dashed #ccc }
article > footer { background: #eee }
article dl > dt { color: #333 }
article dl > dd { color: #555 }
nav + article { margin-top: 0 }

最后一句,nav + & 使用父选择器&后,原本默认的嵌套规则不再适用,而是直接应用 & 组合的结果

2.3 父选择器 

2.3.1 基本使用

父选择器是 Sass 中一种特殊的选择器,用于嵌套选择器中,用来引用外部的选择器;通俗的讲就是,当你使用嵌套的时候,可能你会需要使用到嵌套外层的父选择器,比如为一个元素 添加伪类 (hover、active、before、after) 的时候,可以用 & 代表嵌套规则外层的父选择器,我们举个例子来更直观的感受下:

a {
  &:hover {
    color:red;
  }
  &:active {
    color:blue;
  }
  &:before {
    content:'';
  }
  &:after {
    content:'';
  }
  span {
    &:hover {
      color:green;
    }
  }
}

在上面的 Sass 代码中我们编写了几个伪类,在编译的时候 & 将会被替换为嵌套外层的父选择器,有多层嵌套的话将会把父选择器一级一级的传递下去,最终转换为如下的 CSS 代码:

a:hover {
  color: red;
}
a:active {
  color: blue;
}
a:before {
  content: "";
}
a:after {
  content: "";
}
a span:hover {
  color: green;
}

2.3.2  添加后缀

可以使用 & 向外部选择器添加后缀,举个例子看下:

.box {
  width:100px;
  &-head {
    width:100%;
    &-title {
      color:red;
    }
  }
  &-body {
    width:100%;
  }
  &-footer {
    width:100%;
  }
}

上面这个例子将会转换为如下的 CSS 代码:

.box {
  width: 100px;
}
.box-head {
  width: 100%;
}
.box-head-title {
  color: red;
}
.box-body {
  width: 100%;
}
.box-footer {
  width: 100%;
}

2.4. 占位符选择器 

在 Sass 中这是一种特殊的选择器,称为 "占位符";它以 % 开头,必须通过 @extend 指令调用,如果单独使用的话是不会编译到 CSS 中的,后面会讲到 @extend 指令,这里我们先举个简单的例子感受一下:

%placeholder {
  width:100px;
  height:100px;
  color:red;
  &:hover {
    color:blue;
  }
}

.btn {
  @extend %placeholder;
  font-size: 18px;
}

.btn2 {
  @extend %placeholder;
  font-size: 16px;
}

占位符必须通过 @extend 指令调用才会转换为如下的 CSS 代码:

.btn2, .btn {
  width: 100px;
  height: 100px;
  color: red;
}
.btn2:hover, .btn:hover {
  color: blue;
}

.btn {
  font-size: 18px;
}

.btn2 {
  font-size: 16px;
}

2.5 属性嵌套

当我们在写 CSS 样式的时候,有些 CSS 属性具有相同的命名空间 (namespace),比如定义字体样式的属性: font-size ; font-weight ; font-family ; 它们具有相同的命名空间 font 。再比如定义边框样式的属性:border-radius ; border-color ; 它们具有相同的命名空间 border 。当然还有很多其他这种的属性,为了方便管理和避免重复输入,Sass 允许将属性嵌套在命名空间中,同时命名空间也可以具有自己的属性值,我们举例看一下:

把属性名从中划线-的地方断开,在该属性后边添加一个冒号:,紧跟一个{ }块,把子属性部分写在这个{ }块中。这样就可以实现属性的嵌套。

.box {
  border: {
    radius: 5px;
    color:red;
  }
  font: {
   family:'YaHei';
   size:18px;
   weight:600;
  }
  margin: auto {
    bottom: 10px;
    top: 10px;
  };
}

上面这种写法将会被转换为如下的 CSS 代码:

.box {
  border-radius: 5px;
  border-color: red;
  font-family: "YaHei";
  font-size: 18px;
  font-weight: 600;
  margin: auto;
  margin-bottom: 10px;
  margin-top: 10px;
}

3.运算

 3.1数字运算

在 Sass 中我们可以对数字类型的值进行加减乘除、取整的运算。在使用运算的过程中,一定要注意不能使用不兼容的单位!(在除法运算中除外),什么意思呢?就是说两个数字相加,你不能一个数字单位是 px 另一个数字单位是 em 。还有一点需要注意的是,如果你使用乘法运算,你只需要为其中的一个数值写上单位就好。数值的运算包括加 (+)、减 (-)、乘 (*)、除 (/)、取模 (%),乘法只需要有一个数值带单位即可,还有就是除法稍有特殊,后面会单独讲解,下面我来写一段代码看一下 Sass 的数字运算:

p {
  width: 10px + 20px; // 加法运算 (不能使用不兼容的单位)
  height: 500px +50; // 加法运算无单位的数字可以与有单位的一起使用
  max-width: 800px - 100px; // 减法
  max-height: 400px * 2; // 乘法,一个数值带单位即可
  font-size: 30px % 4; // 模运算
}

上面这段代码转换成 CSS 为:

p {
  width: 30px;
  height: 550px;
  max-width: 700px;
  max-height: 800px;
  font-size: 2px;
}

上面我们对 Sass 数值运算的加减乘和模运算做了演示,在 Sass 的数字运算中还有一个需要特别注意的:减法运算符两边需要加空格或者都不加空格,也就是说运算符的两边是对称的;为什么要这样呢?因为减法运算符 - 不仅仅表示减法,对于负数或者一元否定则只需要在其前面加空格就好,也就是说这个标识符还可以表示负数和一元否定;我们举个例子来看下:

p {
  width: 10px - 5px; // 前后都有空格
  width: 10px-5px; // 前后都没有空格
  width:10px -5px; // 只有前面有空格
}

上面这段 Sass 代码将会被编译成如下的 CSS :

p {
  width: 5px;
  width: 5px;
  width: 10px -5px;
}

我们可以看到上面的代码,如果你只在 - 标识符前面加了空格,其两边不对称是不会对数值进行运算的,所以这个在你写代码的过程中一定要注意!

3.2 除法运算

在 CSS 中,你要知道 / 这个标识符并不是代表除法的,一些 CSS 的属性值是支持使用 / 来分隔的,所以在 Sass 中直接使用 / 也是会当成分隔符来处理。但是呢,在以下情况下,Sass 将会把 / 视为除法运算:

  • 运算符前后的值存储在变量中或由函数返回
  • 运算符和前后的值被圆括号所包裹
  • 值是另外一个表达式的一部分

下面我们来举例看一下:

p {
  width: 210px;
  font-size: 10px;
  border-width: 2px;
  height: 400px;
  max-width: 800px/2;
}

上面这段 Sass 代码我对使用 / 的情况进行了注释,那么它转换为 CSS 代码是:

p {
  width: 210px;
  font-size: 10px;
  border-width: 2px;
  height: 400px;
  max-width: 800px/2;
}

上面我们看到了 Sass 除法运算的使用,还有一种情况是:假如我在两个变量之间使用 / 标识符,而且我又不想对这两个变量进行除法运算,我只是想对这两个变量的值进行分隔而已,那该怎么办呢?那我们需要使用插值 #{} 来将两个变量包裹住即可,关于插值以后的章节会有讲解,这里我们先看一下如何做:

$one: 20px;
$two: 10;
p {
  width: $one / $two; // 没有使用插值,会对变量值进行除法运算
  height: #{$one} / #{$two}; // 使用插值,不会进行除法运算
}

上面这段使用插值的代码将会转换为如下的 CSS 代码:

p {
  width: 2px;
  height: 20px/10;
}

3.3 关系运算

 在 Sass 中关系运算来比较数字与数字间的大小,和数字运算一样,关系运算也是不能使用不兼容的单位。关系运算的返回值是布尔值( true 或 false ),下面我们举个例子看下:

p {
  width: 10px > 5px; // 大于
  width: 10 < 5px; // 小于
  width: 10 >= 5; // 大于等于
  width: 5 <= 5; // 小于等于
  width: 5 == 5; // 等于
}

那么上面这段代码转换成 CSS 如下:

p {
  width: true;
  width: false;
  width: true;
  width: true;
  width: true;
}

3.4 颜色运算

颜色要怎么运算呢?下面我们举个例子看一下:

body {
  color: #020304 + #050203;
}

在进行颜色值计算的时候是分段计算的,也就是 02 + 05 、03 + 02 、04 + 03,我们知道十六进制颜色值是分为三组的(两个数字一组),分别代表红、绿、蓝,所以在做运算的时候也是按照这个来运算的,那么上面这段代码转换成 CSS 为:

body {
  color: #070507;
}

Sass 官方给出了明确的说明:不推荐使用颜色运算,而使用颜色函数。什么原因呢?因为 Sass 颜色运算所产生出来的色值可能和你感知的并不一致,所以不推荐使用。如果你使用了的话, Sass 也是支持的,但是会给出警告,强烈建议用户避免使用颜色运算!所以我们的教程里只是做了个演示,你可以了解下,但在实际开发中不推荐使用颜色运算,可以使用颜色函数来实现你的需求!

3.5 字符串运算

在 Sass 中还允许对字符串进行运算,那对于字符串的运算都有哪些呢?我们先文字描述下:

  • 字符串1 + 字符串2:用于连接字符串,结果会返回包含两个字符串的新字符串,如果这其中一个字符串带引号,那么结果也会带引号,否则就不带引号(带引号的字符串要位于 + 号左侧);
  • 字符串1 / 字符串2:返回一个字符串,这里包含字符串 1 和 字符串 2 ,会用 / 分隔;
  • 字符串1 - 字符串2:返回一个字符串,这里包含字符串 1 和 字符串 2 ,会用 - 分隔。

下面我们使用代码来举例看下:

p {
  color: r + 'ed'; // 带引号的在加号右侧,返回一个不带引号的字符串
  color: 'r' + ed; // 带引号的在加号左侧,返回一个带引号的字符串
  color:r + ed; // 返回一个不带引号的字符串
  color: r/ed; // 返回一个使用 / 分隔的字符串
  color: r-ed; // 返回一个使用 - 分隔的字符串
}

上面这段代码会转换为如下的 CSS 代码:

p {
  color: red;
  color: "red";
  color: red;
  color: r/ed;
  color: r-ed;
}

3.6 布尔运算

Sass 中的布尔运算使用的不是运算符而是单词,所以 Sass 中的布尔运算是 not 、and 和 or,not 的意思是取反,and 的意思是两个都为真则返回真反之则返回假,or 的意思是其中一个为真则返回真。下面我们举个例子来看下:

{
  a: true and true;
  b: true or false; 
  c: true and false;
  d: not false;
}

那么上面这段代码在 Sass 中会转换为:

{
  a: true;
  b: true;
  c: false;
  d: true;
}

3.7 实战经验

在实际的项目开发中,可能最最常用的就是加减乘除的运算了,有时可能会用到字符串运算。在你写 CSS 的时候,相信你一定知道 rem 布局,这个简直太常用了。我们在做 rem 布局的时候经常会设置一个根元素的字体大小,然后其余所有的像素可能都根据这个去计算,所以为了便于维护,我把这个根元素的大小抽离出来作为一个变量,然后在每个元素的样式中对这个变量进行运算就可以了,同时呢,我们还可以在动画中运用一些运算,我们一起来看下:

$root: 28;

html {
  font-size: $root+px;
}
p {
  width: (460rem / $root);
  height: (320rem / $root);
}

@keyframes sacle {
  0% {
    width: (800px - $root);
  }
  50% {
    width: 800px - $root * 2;
  }
  100% {
    width: (800px / $root);
  }
}

上面这段代码我们动态的计算了 rem 还有动画中的一些需要的运算。而且这样做的好处是我们可以随时按需更改变量而不需要重新把没处样式都再手动进行更改,这样就让项目的代码更具有扩展性并且更易于维护。它将会被转换为如下的 CSS 代码

html {
  font-size: 28px;
}

p {
  width: 16.4285714286rem;
  height: 11.4285714286rem;
}

@keyframes sacle {
  0% {
    width: 772px;
  }
  50% {
    width: 744px;
  }
  100% {
    width: 28.5714285714px;
  }
}

在我们的项目中,我们会把 $root 这个变量抽出来到专门维护变量的文件中,然后供项目中所有的页面做运算使用。

4.函数

4.1  函数简介

Sass 为我们提供了很多内置模块,其中就包含了很多函数(包括一些指令),我们可以通过 @use 去加载它们,然后我们就可以调用了,当然还有一些函数可以直接在 CSS 语句中调用,在 Sass 中常用的函数有:

  • 字符串函数
  • 数字函数
  • 列表函数
  • Introspection函数
  • 条件函数
  • Map 函数
  • 颜色函数

4.2.颜色函数

Sass 中提供了非常非常多的颜色函数用来处理颜色值,它们很多需要你具有专业的调色及配色知识才能发挥出作用,所以我们不讲的那么复杂,本节内容中我们只讲几种常见的、比较简单的颜色函数,其他特别复杂的用于调色的函数在以后你可以再慢慢研究。

4.2.1 用于获取通道色值的函数

Sass 提供了可以获取一个色值中红色通道、绿色通道和蓝色通道色值的函数,它们分别是 red(、color)、green(color) 和 blue($color)。你可能还不太了解这三种通道是什么,不要紧,只要知道这三种函数和它的使用就可以。我们举例看下:

blue(#BA55D3)  //=> 211
red(#BA55D3)  //=> 186
green(#BA55D3)  //=> 85

4.2.2 saturate($color, $amount)

saturate($color, $amount) 函数用于调整 $color 的饱和度,第 1 个参数 $color 是一个颜色值,第 2 个参数是 0% ~ 100% 之间的百分数,其返回值也是一个颜色值。

saturate(#BA55D3, 20%)  //=> #c740e8

4.2.3  scale-color(…)

这是一个非常强大的颜色函数,它接收很多个参数,可以调整一个色值的很多属性,包括这个颜色的红、绿、蓝通道,以及亮度等等,我们只能举例来直观的看下:

scale-color(#BA55D3, $red: 15%)  //=> #c455d3  调整红色通道
scale-color(#BA55D3, $blue: 15%)  //=> #ba55da  调整蓝色通道
scale-color(#BA55D3, $lightness: -10%, $saturation: 10%)  //=> #b338d2 调整亮度和饱和度
scale-color(#BA55D3, $alpha: -30%)  //=> rgba(186, 85, 211, 0.7)  调整不透明度

通过上面的例子可以看到颜色函数提供了非常强大的用于调色的函数,驾驭它的前提是你要有非常丰富的调色知识以及一定的美术基础。在实际的项目中我们非常少的用到颜色函数,因为一般都是由公司的 UI 设计师来进行调色,所以作为入门教程,你只需要了解 Sass 中的颜色函数就好。

5.控制指令

5.1.什么是 Sass 控制指令

控制指令,故名思义它是通过条件来控制某些逻辑的,提到条件你首先肯定想到了 if ,没错这是 Sass 控制指令的一种,除了这个还有循环,所以 Sass 一共为我们提供了 4 种控制指令,它们分别是:

  • @if 指令
  • @each 指令
  • @for 指令
  • @while 指令

5.2 @if 指令

5.2.1基本使用

@if 指令是在 @if 后跟一个表达式,然后再接 {} ,如果表达式为 true 则执行 {} 里的代码逻辑,写为 @if { … } ,我们来举例看下:

@mixin avatar($size, $circle: false) {
  height: $size;

  @if $circle {
    width: $size / 2;
  }
}

.square { @include avatar(100px, $circle: true); }

上面我们在 @mixin 中使用了 @if 指令,如果 @if 后面的表达式或变量为 true ,它将执行 {} 里的代码。上面的代码在 .square 的样式中使用了 @mixin ,它将会生成如下的 CSS 代码:

.square {
  height: 100px;
  width: 50px;
}

从上面两段代码的对比中我们看到,我们为 @mixin 传入了 $size 并且 @if 后面的变量为 true,所以它执行了 width: $size / 2 生成的 CSS 就是 width: 50px ,在这里你要重点关注 @if 指令的用法,关于 @mixin 在后面我们会详细讲到,这里你可以先认识下就好。

5.2.2 @else 和 @else if 指令

如果你了解任何的编程语言,那么你一定知道有 if 就会有 else 和 else if ,如果 @if 后面的表达式为 false ,就会判断 @else if 后面的表达式,如果还是 false 则会继续往后走,如果所有表达式都为 false 则最终会执行 @else 后面的 {} 中的代码逻辑

当然 @else if 和 @else是在你需要多条逻辑判断的时候写的,也可以不写,就像上面的代码一样。说了这么多可能你不是很理解,一码胜千言,我们直接将上面的代码段改造下,实际体会一下:

@mixin avatar($size, $circle: 1) {
  height: $size;

  @if $circle == 1 {
    width: $size / 2;
  } @else if $circle == 2 {
    width: $size / 5;
  } @else {
    width: $size;
  }
}


.a { @include avatar(100px); }
.b { @include avatar(100px, $circle: 2); }
.c { @include avatar(100px, $circle: 3); }

上面的代码中我有 3 条判断逻辑对应不同的代码块,然后我在 .a .b .c 中分别调用 @mixin 并传入不同的参数,转换后的 CSS 代码如下:

.a {
  height: 100px;
  width: 50px;
}

.b {
  height: 100px;
  width: 20px;
}

.c {
  height: 100px;
  width: 100px;
}

5.3 @each 指令

@each 指令一般用来循环一个列表或 Map ,它的写法是这样的 @each in { … } ,这其中 expression 表达式返回一个列表或者直接就是一个列表,variable 是列表中的每一项,{} 中是每次循环都会执行的代码,我们举例来看下:

$borders: 2px, 3px, 5px;

@each $bor in $borders {
  .border-#{$bor} {
    border:$bor solid;
  }
}

上面的代码中我们通过 @each 循环一个 $borders 列表,来生成不同的 class 的 border 样式,上面这段代码转换为 CSS 如下:

.border-2px {
  border: 2px solid;
}

.border-3px {
  border: 3px solid;
}

.border-5px {
  border: 5px solid;
}

可以看到上面的写法是不是很方便,这样就直接生成了不用的类名并且对应不同的样式,在 Sass 编程中 @each 也是很常用的指令,所以这个你是一定要会用的,尤其是在写函数的时候

5.4 @for 指令

@for 指令很有意思,它可以设定一个范围然后在这个范围内循环,比如说在 1 ~ 5 这个范围内,或者在 3 ~ 6 这个范围内等等。

它有两种写法 @for from to { … } 或者 @for from through { … },这两种写法中variable 都是每次循环时候的数值,start 都表示开始的边界,end 都表示结束的边界;

这两种写法不同的是 through 包含 start 与 en ,而 to 包含 start 但不包含 end。文字描述难免有些抽象,我们直接举例看下:

$base-color: #036;

// 范围是 1 ~ 3
@for $i from 1 through 3 {
  ul:nth-child(3n + #{$i}) {
    background-color: lighten($base-color, $i * 5%);
  }
}
// 范围是 4 ~ 6
@for $i from 4 through 6 {
  ul:nth-child(3n + #{$i}) {
    background-color: lighten($base-color, $i * 5%);
  }
}

上面的代码我们用的是 through 写法,分别写了 1 ~ 3 范围的循环和 4 ~ 6范围的循环,也就是说循环体中的代码块会分别被计算 3 次,它最终会转换为如下的 CSS 代码:

// 1 ~ 3 范围生成的
ul:nth-child(3n+1) {
  background-color: #004080;
}

ul:nth-child(3n+2) {
  background-color: #004d99;
}

ul:nth-child(3n+3) {
  background-color: #0059b3;
}
// 4 ~ 6 范围生成的
ul:nth-child(3n+4) {
  background-color: #0066cc;
}

ul:nth-child(3n+5) {
  background-color: #0073e6;
}

ul:nth-child(3n+6) {
  background-color: #0080ff;
}

看到转换后的 CSS 是不是感觉使用 @for 指令写起来简直飞快,下面我们在使用 to 写法来举个例子看下:

$base-color: #036;

@for $i from 1 to 3 {
  ul:nth-child(3n + #{$i}) {
    background-color: lighten($base-color, $i * 5%);
  }
}

上面使用 to 写法的代码将会转换为如下的 CSS 代码:

ul:nth-child(3n+1) {
  background-color: #004080;
}

ul:nth-child(3n+2) {
  background-color: #004d99;
}

好了,通过上面的代码可以看出使用 to 写法是不包含 end 边界的。从上面我们举的两个例子不难看出,@for 指令可以极大的简化我们编写冗余繁琐的 CSS ,你自己需要多尝试这个指令来实际感受下。

5.5 @while 指令

@while 指令很像 javascript 中的 while 循环,在 Sass 中 @while 指令的写法是 @while { … } ,当表达式 expression 结果为 true 时就执行 {} 里的代码,直到表达式 expression 结果为 false 。我们举例来看下:

$num: 4;
@while $num >= 1 {
  .box-#{$num} {
    font-weight: 100 * $num;
  }
  $num: $num - 1;
}

从上面的代码可以看出我设定了一个变量 $num 为 4 ,然后每次循环将这个变量 -1 ,知道 $num < 1 的时候会停止循环,也就是说会循环 4 次,我们看下下面转换为 CSS 的代码:

.box-4 {
  font-weight: 400;
}

.box-3 {
  font-weight: 300;
}

.box-2 {
  font-weight: 200;
}

.box-1 {
  font-weight: 100;
}

@while 指令可以让你很方便的控制循环次数,在实际应用中也是非常有用的。

5.6 实战经验

在实际项目中应用 Sass 控制指令的地方还是蛮多的,这里我说一个在我的项目中的应用。我的项目中有个需求是将视口分为 12 等份,然后根据不同的 class 类名来为其宽度设置不同的百分比,这很像其他 UI 库中的栅格系统,我们是这样在项目中实现的:

@for $i from 0 through 12 {
  .width-#{$i} {
    width: (1 / 12 * $i) * 100%;
  }
}

上面这几行代码就实现了我的需求,我需要有 .width-0 到 .width-12 的选择器,同时它们的样式分别是对应的百分比,我直接做了一个从 0 到 12 的循环,然后在其循环体中动态生成 class 和样式,它转换为 CSS 代码如下:

.width-0 {
  width: 0%;
}

.width-1 {
  width: 8.3333333333%;
}

.width-2 {
  width: 16.6666666667%;
}

.width-3 {
  width: 25%;
}

.width-4 {
  width: 33.3333333333%;
}

.width-5 {
  width: 41.6666666667%;
}

.width-6 {
  width: 50%;
}

.width-7 {
  width: 58.3333333333%;
}

.width-8 {
  width: 66.6666666667%;
}

.width-9 {
  width: 75%;
}

.width-10 {
  width: 83.3333333333%;
}

.width-11 {
  width: 91.6666666667%;
}

.width-12 {
  width: 100%;
}

从上面这个示例中是不是可以看出使用控制指令来实现一些需求很方便,这样可以省去你编写大量 CSS 代码的工作,而且计算宽度也仅仅需要设置好公式即可,在我们的项目中有很多类似的用法,至于 @if 指令一般会在函数中做判断来使用。

6.混合指令

6.1 什么是 Sass 混合指令 

 混合指令的出现使你可以定义在样式表中重复使用的样式,这可以使你免去编写过多重复的样式,而且在混合指令 @mixin 中你也可以做一些逻辑处理。混合指令是一个很好用的指令,它将帮你更合理的维护样式代码,学会这种方式写起样式来也很便利,下面我们开始详细的讲解它。

6.2 语法详情

混合指令的写法是 @mixin name { … } 或者 @mixin name(<arguments…>) { … },第一种写法是不传参的指令,第二种写法是传参的指令,我们先来举个简单的例子看下混合指令的样子:

@mixin border {
  border: {
    width: 1px;
    color: #cccccc;
    style: solid;
  }
}

上面我写的这个混合指令是一个不需要传参的,那么它怎么用呢?转换为 CSS 后是什么呢?下面我们从混合指令的定义开始逐一讲解。

6.3 定义和引用混合指令

混合指令的定义是在 @mixin 后跟指令名字和 {} ,在 {} 中你可以写一些样式,同时也可以用一些函数或者前面章节讲的控制指令,现在我们定义一个不接收参数的混合指令和一个接收参数的混合指令:

// 不接收参数的混合指令
@mixin border {
  border: {
    width: 1px;
    color: #cccccc;
    style: solid;
  }
}
// 接收参数的混合指令
@mixin font($size: 12px, $weight: 100) {
  font: {
    family: "Myfont";
    weight: $weight;
    size: $size;
  }
}
.box {
  // 引用混合指令
  @include border;
}
.item {
  // 引用混合指令并传参
  @include font(20px, 500);
}

上面的代码中我们分别定义了两个简单的混合指令,然后在 .box 和 .item 的样式中通过 @include 引用混合指令,在 @include 后直接跟混合指令的名称就可以引用了,传参如上面代码所示,那么上面这段代码将会转换为如下的 CSS 代码:

.box {
  border-width: 1px;
  border-color: #cccccc;
  border-style: solid;
}

.item {
  font-family: "Myfont";
  font-weight: 500;
  font-size: 20px;
}

看到转换后的 CSS 代码是不是感觉混合指令很强大,我们把指令写好后,可以在任何需要它的地方来使用,而且我们只需要传参就可以生成各种各样的样式代码。还有一点需要注意的是,在 Sass 中,@minxin 后面的名字将连字符和下划线视为是相同的。

6.4 混合指令的参数

在上面的代码中我们已经知道了混合指令是可以传参数的,参数是在指令名后面由括号括起来的变量名列表,混合指令每次调用都可以操作这些传入的参数。

这些参数只要声明了就必须传入,如果你想让某个参数成为可选的,你需要为这个参数赋一个默认值,赋默认值的方法就像变量声明赋值一样,直接在变量名后面加冒号然后跟默认值。我们举例看下:

// 没有赋默认值的参数
@mixin font-one($size, $weight) {
  font: {
    family: "Myfont";
    weight: $weight;
    size: $size;
  }
}
// 赋默认值的参数
@mixin font($size: 12px, $weight: 100) {
  font: {
    family: "Myfont";
    weight: $weight;
    size: $size;
  }
}

从上面的代码中可以看出是否赋默认值的区别,默认值还可以引用前面的参数。除了默认值,在传入参数的时候我们还可以按名称传入参数,什么意思呢,我们直接举例看下:

@mixin font($size: 12px, $weight: 100) {
  font: {
    family: "Myfont";
    weight: $weight;
    size: $size;
  }
}
.item {
  // 按名称传入参数
  @include font-one(20px, $weight: 800);
}

上面这段代码将会转换为如下的 CSS 代码:

.item {
  font-family: "Myfont";
  font-weight: 800;
  font-size: 20px;
}

按名称传入参数使我们可以更好的控制混合指令接收的参数,但这个方法还是尽量少用,因为参数名有时在多人开发的时候可能不是一成不变的!

有时候 @mixin 接收的参数个数你可能不不清楚有多少个,那么你可以将最后一个参数以 … 结尾,那么所有额外的参数都将传给该参数,然后在 @mixin 里来获取所有参数,我们举个例子直观的感受下:

@mixin fonts($s, $familys...) {
  font:{
    size: $s;
    family: $familys;
  }
}
.p {
  @include fonts(12px, "one", "two", "three")
}

上面这段代码转换为 CSS 代码如下:

.p {
  font-size: 12px;
  font-family: "one", "two", "three";
}

除此之外,@mixin 还可以通过参数列表接收任意参数,然后通过 meta.keywords() 这个函数来使用传入的这些参数,我们下面举例看下:

@mixin args($args...) {

  @each $key, $val in keywords($args) {
    font: $key $val;
  }
}

.p {
  @include args($one: 1, $two: 2, $three: 3)
}

上面这个例子是为了让你更直观的看到这种传参方式,实际样式中不会这么写,上面这段代码我们通过循环 keywords() 函数返回的值来使用传入的参数,它将会被转化成如下的 CSS 代码:

.p {
  font: one 1;
  font: two 2;
  font: three 3;
}

6.5 实战经验

我们的项目是一个 Vue 单页应用,在我们的实际项目中有专门的 mixin.scss 文件来管理全局的 @mixin 指令,这里我从中截取出一部分来展示下:

@mixin border ($width: 1px, $color: #cccccc, $style: solid) {
  border: {
    width: $width;
    color: $color;
    style: $style;
  }
}
@mixin font($size: 12px, $weight: 100, $familys...) {
  $family: "Times";
  @if length($familys) > 0 {
    $family: $familys;
  }
  font: {
    size:$size;
    weight: $weight;
    family: $family;
  }
}

@mixin btn($type: "main") {
  border-radius: 4px;
  @if $type == "small" {
    width: 60px;
    height: 20px;
    background-color: #e5e5e5;
    color: #ffffff;
    &:hover {
      background-color: #4AA1FF;
    }
  } @else if $type == "disable" {
    width: 80px;
    height: 30px;
    background-color: #CCCCCC;
    color: #ffffff;
  } @else {
    width: 80px;
    height: 30px;
    background-color: #e5e5e5;
    color: #ffffff;
    &:hover {
      background-color: #4AA1FF;
    }
  }
}

从上面的代码可以看出,我定义的全局的 @mixin 有关于 border 样式的,有关于 font 样式的,还有一个我们自己封装的 button 样式,这样在项目的任何需要写这些样式的地方直接应用这些指令就可以了,而不需要编写大量的 CSS 样式,下面我截取一部分在某些页面中使用这些指令的代码:

// 使用 border 混合指令
.normal-border {
  @include border;
}
.error-border {
  @include border(2px, red, solid);
}
// 使用 font 混合指令
.main {
  @include font(24px);
  .item {
    @include font(16px, 600, "serif", "Roman", "Times");
  }
}
//  使用 button 混合指令
.btn {
  &-main {
    @include btn(); 
  }
  &-disable{
    @include btn("disable");
  }
  &-small{
    @include btn("small");
  }
}

上面的代码转换为 CSS 会非常的长,这里我就不贴出转换后的 CSS 代码了。

你可以仔细看下这些代码,看看是怎么封装和使用的,在公司的实际项目中,如果使用了 Sass ,你一定会看到类似的这些封装,当然你可能也会自己封装这些;你可以对照这上面两段定义混合指令和使用混合指令的代码来复习本节的内容,然后自己尝试这将它转换为 CSS 以便更好的理解!

7.函数指令

7.1 什么是 Sass 函数指令

函数指令也叫自定义函数让你可以容易的处理各种逻辑和定义复杂的操作,而且你可以在任何需要的地方复用函数,这使得我们可以抽离出来一些常见的公式或者逻辑,我们先来看下它长什么样,代码如下:

// 定义函数
@function a() {
  @return "a"
}
// 使用函数
.p {
  font: a();
}

回忆一下,上面的代码在之前的章节中也出现过,这就是函数指令,定义好一个函数后我们就可以使用了,下面我们开始详细讲解函数指令。

函数指令是通过 @function 来定义,它的写法是 @function name(arguments…){},@function 后面跟函数名,然后是一个 () ,() 里面是这个函数接收的参数,可以接收也可以不接收,最后是 {} 中放的是你的逻辑代码。函数名将连字符和下划线视为相同,也就是说 a_b 和 a-b 是同一个函数。我们举例看下:

@function fun-name() {
      // 在这里编写逻辑代码
}

7.2 函数的参数

函数指令的参数和之前我们讲的混合指令的参数很像,函数如果接收参数那么使用的时候就必须传入这些参数,但是你可以定义默认值使参数成为可选的,我们举例来看下:

// 有默认值的参数
@function a($arg: 1) {
  @return $arg;
}
// 无默认值的参数
@function b($arg) {
  @return $arg;
}
.p {
  font: a();
  font: b(4);
}

上面的代码转换为 CSS 如下:

.p {
  font: 1;
  font: 4;
}

要注意的是,为参数设置的默认值,也可以引用前面的参数或者是任何表达式。

7.3 接收任意数量的参数

和前面章节讲的 @mixin 类似,函数指令也可以接收任意数量的参数,同样是将最后一个参数以 … 结尾,我们来举例看下:

@function fonts($familys...) {
  @return  $familys;
}
.p {
  font: fonts("one", "two", "three")
}

上面这段代码转换为 CSS 如下:

.p {
  font: "one", "two", "three";
}

同样的函数的参数也可以接收任意的参数列表,就像之前在混合指令章节讲的一样,可以用过 meta.keywords() 来获取和使用这些参数,不过这个我们一般不是很常用。

7.4 @return

在前面的代码中,可以看到很多次我们在函数指令中使用了 @return。@return 指令表示作为函数调用结果的值,说的简单点就是这个函数的返回值,这和在 javascript 的函数中使用 return 很类似。

在 Sass 中 @return 指令只能在 @function 中使用,并且每个 @function 都必须以 @return 结尾! 在 @function 的逻辑代码中,如遇到 @return 会立即结束函数并返回其结果,这在一些 @if 判断的情况下很有用。我们举例看下:

@function a($str: "a") {
  @if $str == "a" {
    @return 10px;
  } @else if $str == "b" {
    @return 20px;
  } @else if $str == "c" {
    @return 30px;
  } @else {
    @return 40px;
  }
}

p {
  padding: a();
  width: a("f");
  height: a("c");
  margin: a("b");
}

从上面的代码中我们可以可看到,在函数 a 中,我们根据不同的参数返回不同的结果,然后在 p 的样式中通过传入不同的参数来获取不同的结果。上面这段代码会转换为如下的 CSS 代码:

p {
  padding: 10px;
  width: 40px;
  height: 30px;
  margin: 20px;
}

从上面的代码中我们可以可看到,在函数 a 中,我们根据不同的参数返回不同的结果,然后在 p 的样式中通过传入不同的参数来获取不同的结果。上面这段代码会转换为如下的 CSS 代码:

p {
  padding: 10px;
  width: 40px;
  height: 30px;
  margin: 20px;
}

7.5 实战经验

在实际的项目中使用函数指令是必不可少的,我们会定义很多函数来帮助我们解决逻辑问题,一般我们会独立抽出来一个 function.scss 文件来管理整个项目中的函数指令,一般这些函数都是根据你的项目特性以及样式需要封装出来的。

由于函数一般是和你的业务强相关的,一般就是为自己的项目定义一些工具和方法,这里我就不贴出代码示例了。在实际的应用中,这个函数指令就是需要你“随机应变”,按需封装和使用!

8.继承

8.1 什么是 Sass 继承

继承,我们也叫做代码重用,在 Sass 中支持对样式进行继承。

首先我们看一段代码,看看在 Sass 中的继承是长什么样子的:

.a {
  width: 10px;
}

.b {
  @extend .a;
  height: 10px;
  color: red;
}

从上面的代码中可以看见,我在 .b 的样式中使用 @extend 继承了 .a 的样式,那么这段代码转换为 CSS 如下:

.a, .b {
  width: 10px;
}

.b {
  height: 10px;
  color: red;
}

上面的代码中我们看到了 Sass 中继承的样子,它的写法是 @extend selector 也就是在 @extend 后面跟一个选择器,表示继承这个选择器的样式,下面我们详细讲解下它的语法。

@extend 会包含包含扩展的样式规则,同时在 Sass 中它会确保复杂的选择器是交错的,这样无论你的 DOM 元素是如何嵌套的它都能保证正常工作。它还可以根据实际情况将选择器组合在一起,可以更智能的处理选择器以及包含伪类的选择器。我们举个稍复杂点的例子来看下:

.a {
  width: 100px;
  height: 200px;
  background-color: orange;
  &:hover {
    background-color: green;
  }
  .link {
    width: 50%;
    height: 50%;
    color: red;
    &:active {
      color: blue;
    }
    i {
      font-size: 18px;
      font-weight: 600;
    }
  }
}

.b {
  @extend .a;
  width: 400px;
  height: 200px;
}

我们仔细解读上面的代码,我为 .a 写了一大堆的样式,包括它的子元素以及它的伪类;而 .b 下面有同样的子元素,同样的伪类别,只是宽高不同,那么我直接在 .b 中继承 .a 的样式,它会转换为什么样子的代码呢?它转换为 CSS 的代码如下:

.a, .b {
  width: 100px;
  height: 200px;
  background-color: orange;
}
.a:hover, .b:hover {
  background-color: green;
}
.a .link, .b .link {
  width: 50%;
  height: 50%;
  color: red;
}
.a .link:active, .b .link:active {
  color: blue;
}
.a .link i, .b .link i {
  font-size: 18px;
  font-weight: 600;
}

.b {
  width: 400px;
  height: 200px;
}

从上面转换成 CSS 的代码我们可以看出,引用相同样式的部分都以逗号做了分隔,在 CSS 中使用逗号的含义你应该很了解,继承 @extend 就可以为你自动创建这些组合,提取相同的样式,所以如果有选择器使用了相同的样式,请使用继承的方式来实现!

8.2 占位符选择器

在 Sass 中有一种特殊的选择器叫占位符选择器,它的写法像我们写的 id 或 class 选择器一样,只不过占位符选择器是以 % 开头的。在 Sass 中你单独使用这种选择器是不会转换为 CSS 的,只能是通过 @extend 来使用

比如说有时候你想编写一个可扩展的样式,然后在各处继承它,你就可以使用占位符选择器,我们结合实际的例子来看下:

%placeholder {
  box-sizing: border-box;
  border-top: 1px #666666 solid;
  width: 100%;

  &:hover { border: 2px #999999 solid; }
  &:active {color: blue;}
}

.buttons {
  @extend %placeholder;
  color: #4285f4;
}
.btn {
  @extend %placeholder;
}

从上面的代码中看到,我通过占位符选择器 %placeholder 定义了一堆样式,然后在其他的样式表中继承它,这个告诉你一个简单的理解方式,占位符选择器你就理解为一个虚拟的选择器,这个名是不会编译到 CSS 中的,最终编译出的选择器名是根据你使用继承的选择器名来定的。上面这段代码会转化为如下的 CSS 代码:

.btn, .buttons {
  box-sizing: border-box;
  border-top: 1px #666666 solid;
  width: 100%;
}
.btn:hover, .buttons:hover {
  border: 2px solid;
}
.btn:active, .buttons:active {
  color: blue;
}

.buttons {
  color: #4285f4;
}

从上面的代码中可以看到,编译成 CSS 后 %placeholder 这个选择器不见了,但它的样式被继承了,这就是占位符选择器结合继承 @extend 的用法

8.3 在 @media 中使用 @extend

如果你需要在 @media 中使用继承,一定要注意使用方式!如果你在外部定义样式,然后在 @media 内部继承外部的样式,Sass 是会报错的。我们首先举个错误的例子看下:

.error {
  border: 1px red solid;
  background-color: red;
}

@media screen and (max-width: 600px) {
  .btn-error {
    @extend .error;
  }
}

如上面的代码所示,这样的写法在 Sass 中是会报错的,也不会编译成功。 Sass 规定继承只能在给定的媒体上下文中使用,所以正确的写法如下:

@media screen and (max-width: 600px) {
  .error {
    border: 1px red solid;
    background-color: red;
  }
  .btn-error {
    @extend .error;
  }
}

上面这个正确的写法将会被编译为如下的 CSS 代码:

@media screen and (max-width: 600px) {
  .error, .btn-error {
    border: 1px red solid;
    background-color: red;
  }
}

8.4 实战经验

在实际的项目中,继承是非常好用的一个功能,不过这个就需要你自己根据需求来判断是否使用,因地制宜,而且尽量把公共的样式提取到一个单独的文件来维护。

还有一个需要注意的是除了继承 @mixin 也是可以封装和复用样式的,那么什么时候使用 @mixin 什么时候使用 @extend 呢?假如你需要使用参数来配置样式的时候,也就是需要传参数的时候毫无疑问使用 @mixin 。但如果你只是需要复用一部分样式那么还是使用继承会更方便些.

9.导入

9.1.语法详情

Sass 的导入和 CSS 中的导入语法类似,只不过在 Sass 中可以导入用逗号分隔的多个文件, 我们举个例子看下:

@import 'a.scss', 'b.scss';

上面的代码意思是导入 a.scss 和 b.scss 文件,那么导入后 a 和 b 中的任何mixin 、函数和变量都是可以使用的。

我们知道在 CSS 中也有 @import 语句,在以下几种情况 Sass 会认为 @import 是 CSS 语句:

  • 使用 url()
  • 文件的扩展名是 .css
  • @import 包含 media queries
  • 文件名以 http:// 开头

在使用的时候要注意上面的几种情况,如果导入的扩展名是 .scss 或 .sass 那么肯定用的是 Sass 提供的 @import 。如果导入文件没有指定文件扩展名,那么 Sass 会尝试寻找文件名相同的扩展名为 .sass 或 .scss 的文件。

9.2 加载路径

Sass 允许我们自行提供文件的加载路径,在我们导入文件的时候,Sass 总是会相对于当前文件进行解析,如果没有加载到则会使用加载路径。假如我们将加载路径设置为 node_modules/public/sass ,那么我们使用如下的导入方式:

@import 'a';

假如当前目录下没有 a.scss 文件,那么 Sass 就会去加载 node_modules/public/sass/a.scss ,这就是使用了加载路径,不过这种方式我们在项目中极少应用,你只需要了解即可。

9.3 部分导入

什么是部分导入呢? 我的理解是局部的使用导入,也就是说可以仅导入 Sass 或 Scss 文件,而不将它们编译为 CSS, 那么应该怎么做呢?假如我要导入一个 my.scss 文件,我不希望将它编译为 CSS ,那么需要使用下划线开头的文件名,也就是说需要改名为 _my.scss ,然后使用如下导入代码:

@import 'my';

上面的代码导入的就是 _my.scss 文件,并且不会将它编译为 CSS 。另外需要注意的是:不可以同时存在带有下划线和不带下划线的同名文件!

9.4 索引文件

在 Sass 中什么是索引文件呢?_index.scss 文件,那它有什么用呢?假如我有一个 my 目录,这个目录下有两个文件,一个是 a.scss 一个是 _index.scss ,那么我使用如下文件导入代码:

@import 'my';

那么上面的代码导入的就是 _index.scss 文件,也就是说 _index.scss 是这个目录下的默认文件,这就想你在写 vue 或者 html 中目录下的 index 文件类似。

9.5 使用 @use 替代 @import

Sass 官方团队不鼓励使用 @import 导入,并且在未来几年将逐步淘汰它,并最终将 @import 从 Sass 中完全删除。所以使用 @use 是官方团队更推荐的方式,下面我们开始讲解使用 @use 导入。

@use 与 @import 的语法基本相同,我们先看一个简单的使用 @use 导入的例子:

@use 'my/a.scss';
@use 'my/b';

从上面的代码中可以看到其使用方式与 @import 是相同的,那么为什么还要替换掉 @import 呢? 主要是以下几个原因你需要了解下:

  • @import 会使得所有变量、mixin 和函数都可以全局访问,这使开发者很难去维护这些定义的东西。
  • 因为所有的都是全局的,那么 Sass 必须为所有的成员添加前缀,以避免命名冲突。
  • @extend 也是全局的,这样将很难预测哪些样式将被扩展。
  • 每次使用 @import 时,每个样式表都会被执行,这会增加编译时间
  • 无法定义下游样式表无法访问的私有成员。

基于上述的这些原因,Sass 官方团队将会逐渐淘汰 @import,可以使用 @use 替代,语法是相同的,所以我们在 v4.x.x 及以上的版本中尽量使用 @use 来导入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值