Sass基础入门

目前公司多端项目开发中使用的 js/css文件结构如下:
Sass基础入门_第1张图片
如果你也是第一次接触这种,你会好奇的发现它的.css文件命名方式有点不同,要想搞清楚这个命名,主要了解两个东西,一个是 CSS Modules, 通过该链接我们了解到 CSS Modules 就是以模块化的方式来管理 CSS,它可以避免 CSS 样式全局污染的问题,因为使用CSS Module编译后的class类名会变成一个唯一的字符串, 它是采用 css-loader 默认的哈希算法[hash:base64]来生成的唯一值。另一个需要了解的就是 .scss,它是什么后缀呢,搜索一下就知道它是 Sass 格式的后缀,本文主要记录一下Sass的入门知识。

文章目录

      • 什么是 Sass?
      • 为什么要使用 Sass?
      • 在前端项目中安装 Sass
      • Sass 嵌套
        • 样式嵌套
        • 嵌套选择器列表
      • 嵌套组合符选择器
        • 嵌套中引用父选择器
        • 嵌套中向父选择器添加后缀
        • 嵌套中使用占位符选择器
        • 属性嵌套
      • Sass 变量
        • 变量的声明定义
        • 变量的命名格式
        • 变量的引用
        • 变量的作用域
      • Sass 数据类型
        • Number 类型
        • Boolean 类型
        • String 类型
        • Colors类型
        • Lists 类型
        • Maps 类型
        • Null 类型
      • Sass 运算
        • 数字运算
        • 圆括号
        • 除法运算
        • 关系运算
        • 颜色运算
        • 字符串运算
        • 布尔运算
      • Sass 插值
        • 在选择器名字中使用插值
        • 在属性名中使用插值
        • 在属性值中使用插值
        • 在注释中使用
      • Sass 函数
        • 字符串函数
        • Math 函数
        • 列表函数
        • 条件函数
        • Map 函数
        • 颜色函数
      • Sass中的 @-Rules 指令
        • @use 和 @import
        • @forward
        • @mixin 和 @include
        • @function
        • @extend
        • @error、@warn、@debug
        • @at-root
        • 控制规则
      • Sass 控制指令
        • @if、@else 和 @else if 指令
        • @each 指令
        • @for 指令
        • @while 指令
      • Sass 函数指令
        • 函数定义
        • 函数参数
        • 接收任意数量的参数
        • @return 指令
      • Sass 混合器指令
        • 定义和引用混合指令
        • 混合器传参
        • 导入内容到混合器
      • Sass 继承
        • 占位符选择器
        • 在 @media 中使用 @extend
      • Sass 导入
        • 加载路径
        • 部分导入
        • 索引文件
        • 导入 CSS
        • 嵌套导入
        • 使用 @use 代替 @import
      • Sass 输出格式
      • Sass 注释
      • Less又是什么?
        • Sass 与 Less 的区别
        • Sass 与 Less 的相同之处
      • 关于 Dart Sass

什么是 Sass?

  • Sass 是一个稳定的、强大的、专业级的 CSS 扩展语言
  • Sass 是基于 CSS 扩展出来的,是一个 CSS 预处理器
  • Sass 在编译后会转换为 CSS 的,它只在编译时去做处理,而不是在运行时

Sass 的缩排语法,对于写惯 css 前端的 web 开发者来说很不直观,也不能将 css 代码加入到Sass里面,因此Sass语法进行了改良,Sass 3 就变成了Scss(Sassy CSS)SCSSCSS 语法的扩展。即每一个有效的 CSS 也是一个有效的 SCSS 语句,与原来的语法兼容,只是用{}取代了原来的缩进。

因此我们有时看到的是.sass 有时是.scss, 其实它俩是一样的,只不过一个是SASS的后缀, 一个是SCSS的后缀,都是同一个东西,区别有点像iPhone和iPhone Plus。

为什么要使用 Sass?

主要是为了 提高开发效率提高代码可维护性!在大型的 web 应用开发中我们一般会编写大量的样式代码,Sass 在 CSS 的基础上提供了变量、嵌套 (nesting)、混合 (mixins) 、函数、指令等功能,使得我们可以更高效地编写样式,同时你还可以像编写 JS 一样来灵活地控制你的样式代码,这给 CSS 的开发带来了极大的便利!

在前端项目中安装 Sass

首先要知道两个类库: node-sassdart-sass ,这两个都是Sass 的实现 ,本身 Sass 是使用 Ruby 语言写的,但是它提供了很多接口以方便其他语言来集成和封装,node-sassdart-sass 就是基于 LibSass( Sass 的 C 版本) 封装而来的。

它们和 LibSass 的关系就是橘子和橘子汁的关系:
Sass基础入门_第2张图片
由于很少有人用dart来开发前端项目,所以前端一般都是用 node-sass 这个库

安装 node-sass 一般使用 npm 或者 yarn 来安装:npm install node-sass

安装过程会去下载,如果下载失败,可能需要配置 npm 的源为淘宝镜像:

npm install -g mirror-config-china --registry=http://registry.npm.taobao.org

前端的项目是使用 Webpack 来构建,那么我们还需要使用 sass-loader 来编译项目中的 Sass ,所以我们需要在 Webpack 的配置中配置 sass-loader ,配置如下:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{
      test: /\.scss$/,
      use: [{
          loader: "style-loader" // 将 JS 字符串生成为 style 节点
      }, {
          loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
      }, {
          loader: "sass-loader" // 将 Sass 编译成 CSS
      }]
    }]
  }
};

执行如下命令安装 sass-loader :

npm install sass-loader --save-dev

总结:
Sass基础入门_第3张图片

Sass 嵌套

Sass使得你可以在父选择器的样式中直接编写子元素的样式,然后在一个子元素的样式中再去编写孙元素的样式,可以一层一层的嵌套着去写样式。

样式嵌套

用编写一个导航的样式来举例,假定我们的导航 nav 下面有 ul 标签,ul 标签下又有 li 标签,li 标签下呢又有 a 标签,下面我使用 Sass 来处理导航中的样式:

 nav {
  width:200px;
  background:white;
  ul {
    width:100%;
    background:red;
    li {
      width:100%;
      background:blue;
      a {
        color:green;
        font-size:20px;
      }
    }
  }
}

由于Sass编译后会转换生成CSS,可以通过这个网站 在线Sass转CSS 来在线查看转换后的结果。

上面的Sass 代码最终转换 CSS 代码如下:

nav {
  width: 200px;
  background: white;
}
nav ul {
  width: 100%;
  background: red;
}
nav ul li {
  width: 100%;
  background: blue;
}
nav ul li a {
  color: green;
  font-size: 20px;
}

写起来是不是方便很多,但要注意:嵌套的越深,那么编译生成的 CSS 的语句就越多,同时消耗的资源也会越多。

嵌套选择器列表

由逗号分隔的选择器列表会被 Sass 组合到一个选择器列表中:

.alert, .warning {
  ul, p {
    margin-right: 0;
    margin-left: 0;
    padding-bottom: 0;
  }
}

转换后的CSS:

.alert ul, .alert p, .warning ul, .warning p {
  margin-right: 0;
  margin-left: 0;
  padding-bottom: 0;
}

嵌套组合符选择器

嵌套使用带有选择符的选择器,我们可以将选择符放在外部选择器的末尾,或者内部选择器的开始位置:

ul > {
  li {
    list-style-type: none;
  }
}

h2 {
  + p {
    border-top: 1px solid gray;
  }
}

p {
  ~ {
    span {
      opacity: 0.8;
    }
  }
}

转换后的CSS:

ul > li {
  list-style-type: none;
}

h2 + p {
  border-top: 1px solid gray;
}

p ~ span {
  opacity: 0.8;
}

嵌套中引用父选择器

当你使用嵌套的时候,可能你会需要使用到嵌套外层的父选择器,比如为一个元素 添加伪类 (hover、active、before、after) 的时候,可以用 & 代表嵌套规则外层的父选择器。

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

转换后的CSS:

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

嵌套中向父选择器添加后缀

向外部选择器添加后缀还是使用 & 符号

.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%;
}

嵌套中使用占位符选择器

在 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;
}

转换后的CSS:

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

.btn {
  font-size: 18px;
}

.btn2 {
  font-size: 16px;
}
属性嵌套

属性嵌套指是 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;
}

小结:
Sass基础入门_第4张图片

Sass 变量

Sass中可以定义变量来表示属性值,以便提高复用,方便修改。

举个例子,假如我们项目中很多地方要设置一个字体颜色为红色,那么我们完全可以把这个颜色抽出来作为一个变量,然后在所有需要设置字体颜色的地方引用这个变量。这样有一个好处就是,假如产品大大说要修改所有字体颜色的时候,我们就不需要每处都去修改了,直接更改变量的值就 OK 了。

变量的声明定义

定义变量以 $ 开头: $variable

$variable: red;

.title {
  color: $variable;
}
h1 {
  color: $variable;
}

转换后的CSS:

.title {
  color: red;
}

h1 {
  color: red;
}

Sass 变量的值,支持原来任何 CSS 的属性值都可以作为 Sass 变量的值

$font-family: "YaHei""Myriad Pro"; // 字体的属性值
$color: red; // 颜色的属性值
$width: 20px; // 宽度的属性值
$border: 1px solid #000000; // border的属性值
$use-flex-grow: 1; // flex-grow的属性值

变量值的类型可以是:

  • 字符串
  • 数字
  • 颜色值
  • 布尔值
  • 列表
  • Null 值
变量的命名格式

没有什么特殊要求,只要是语义化,方便理解就行,如定义变量名为 $theme-color,页面中的主要边框颜色,定义为 $main-border-color 等等,也可以根据公司团队的规范要求。

在 Sass 中,使用中划线 “-” 和下划线 “_” 这两种方式是兼容的,也就是说你用中划线声明的变量,也可以使用下划线去引用,比如我定义了变量 $theme-color ,我在引用的时候也可以写成 $theme_color

变量的引用
$main-bg-color: blue;
$main-border: 1px solid #cccccc;
$main-font-size: 18px;
$error-text-color: red;
$body-color: $main-bg-color;
body {
  background-color:$body-color;
}
.content {
  font-size:$main-font-size;
  background-color:$main-bg-color;
  border:$main-border;
}
.error {
  font-size:$main-font-size;
  color:$error-text-color;
}

$body-color 这个变量,可以发现,在声明变量时,变量的值也可以引用其他变量

转换后的CSS:

body {
  background-color: blue;
}

.content {
  font-size: 18px;
  background-color: blue;
  border: 1px solid #cccccc;
}

.error {
  font-size: 18px;
  color: red;
}

变量的作用域

与JS一样,变量作用域也分为全局和局部,在局部作用域当中,即便同名,全局变量也不受影响

$main-color: red;
h1 {
  $main-color: green; // 局部变量
  color:$main-color;
}
h2 {
  color:$main-color;
}

转换后的CSS:

h1 {
  color: green;
}

h2 {
  color: red;
}

如果局部的变量想修改或覆盖全局变量,使用 !global 标识符:

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

这样编译之后两个都会是green

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

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

最后编译的时候会使用最后一次声明的变量值,也就是 green , 但是有一种情况:在实际的项目开发中,假如需要你来写一段公共的 Sass 代码给其他开发者使用,那么如果你的代码中声明了 $main-color 这个变量,那么其他开发者在自己页面也声明了 $main-color 这个变量,并且他是在导入你的这段代码之前声明的,那么他的就会被覆盖掉,这是不行的!

可以使用 !default 标识符来解决:

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

转换后的CSS:

h1 {
  color: red;
}

如果其他开发者没有声明这个变量,就会使用 green 这个用户自己定义的变量值。

实际项目中会抽取一般都会抽离出 1~n 个文件来专门声明 Sass 变量(抽离出几个文件视项目大小而定)

我们一般会在 styles 目录下新建一个 variables.scss 文件来管理声明的全局变量

Sass基础入门_第5张图片
小结:
Sass基础入门_第6张图片

Sass 数据类型

Number 类型

在 Sass 中,数字类型支持普通数字,科学计数法,还支持带单位的写法,如 8px 这种,即 Number 类型可能有单位也可能没有单位,而且这是非常常用的。

$main-height: 80px; // 带有单位的 Number 类型
$main-flex-grow: 1; // 不带单位的 Number 类型
$main-num: 5e3; // 使用科学计数法的数字
$main-max-height: 2 * 80px; // 运算
.box {
  height: $main-height;
  flex-grow: $main-flex-grow;
  width: $main-num;
  max-height: $main-max-height;
}

转换后的CSS:

.box {
  height: 80px;
  flex-grow: 1;
  width: 5000;
  max-height: 160px;
}

Boolean 类型

Boolean 类型无非就两个值,true 和 false ,当然除了直接写的 true 和 false 外,也可以是一些等式、关系运算、或内置函数的结果,不过这些结果最终还是 true 或 false 。一般来说 Boolean 类型都会结合 Sass 判断或者函数来使用,真正直接写在样式中基本是没有的,所以这里你只要知道这个类型就可以。

String 类型

在 Sass 中支持 带引号不带引号 两种方式的字符串,比如 “a” 或者 a ,还支持转义符 **

$string-one: 'Yahei'; // 带引号的字符串
$string-two: Yahei; // 不带引号的字符串
$string-three: '\"yahei'; // 使用转义符的字符串
.box {
 font:$string-one; 
 font-family: $string-two;
 font: $string-three;
}

转换后的CSS:

.box {
  font: "Yahei";
  font-family: Yahei;
  font: '"yahei';
}

Colors类型
$color-one: #ffffff;
$color-two: red;
$color-three: rgb(204, 102, 153);
$color-four: rgba(107, 113, 127, 0.8);
.box {
  color: $color-one;
  background-color: $color-two;
  border-color: $color-three;
  color: $color-four;
}

转换后的CSS:

.box {
  color: #ffffff;
  background-color: red;
  border-color: #cc6699;
  color: rgba(107, 113, 127, 0.8);
}

Lists 类型

Lists 类型就是数组,列表中包含一系列的值,在 Sass 中列表的元素可以使用逗号或者空格来分隔

$font-list: 'Georgia','Serif'; // 通过逗号分隔元素
$border-list: 1px 2px 3px 4px; // 通过空格分隔元素
$padding-list: 3px,3px 4px 4px; // 混用(不建议)

Maps 类型
$textStyleMap: (
  'font-family': 'Georgia',
  'font-weight': 600,
  'font-size': 18px,
  'text-align': center
);
p {
  font-family: map-get($textStyleMap, 'font-family');
  font-weight: map-get($textStyleMap, 'font-weight');
  font-size: map-get($textStyleMap, 'font-size');
  text-align: map-get($textStyleMap, 'text-align');
}

转换后的CSS:

p {
  font-family: "Georgia";
  font-weight: 600;
  font-size: 18px;
  text-align: center;
}

Null 类型

在 Sass 中它表示缺少值,通常由函数返回。如果说在列表中有 null ,那么在生成 CSS 的时候会忽略该空值。

小结:

Sass基础入门_第7张图片

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;
}

需要注意的地方:

  • 不能使用不兼容的单位!(在除法运算中除外),如两个数字相加,不能一个数字单位是 px 另一个数字单位是 em
  • 乘法运算,只需要为其中一个数值写上单位即可
  • 减法运算,符两边需要都加空格或者都不加空格,也就是说运算符的两边是对称

上面最后一条,是因为减法运算符 - 不仅仅表示减法,还能表示负数
例如:

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

转换后的CSS:

p {
  width: 5px;
  width: 5px;
  width: 10px -5px;
}
圆括号

跟通常意义上的四则运算中的圆括号一样,可以来控制运算顺序,Sass里一般除法中比较有用。

除法运算

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

  • 运算符前后的值存储在变量中或由函数返回
  • 运算符和前后的值被圆括号包裹
  • 值是另外一个表达式的一部分
$one: 20px / 2;
$two: 10px;
p {
  width: 200px + 100px / 10; // 值是另外一个运算表达式的一部分
  font-size: $one; // 前后的值存储在变量中或由函数返回
  border-width: $two / 5; // 前后的值存储在变量中或由函数返回
  height: (800px / 2); // 被圆括号所包裹
  max-width: 800px / 2; // 会被当作分隔符来处理,而不是除法运算
}

转换后的CSS:

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

还有一种情况是:假如我在两个变量之间使用 / 标识符,而且我又不想对这两个变量进行除法运算,我只是想对这两个变量的值进行分隔而已,那该怎么办呢?那我们需要使用插值 #{} 来将两个变量包裹住即可:

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

转换后的CSS:

p {
  width: 2px;
  height: 20px/10;
}
关系运算

很少直接用,看例子:

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;
}

在 Sass 中使用相等运算符去做比较的时候,对于数字类型,数字具有相同的值和相同的单位,或者在单位之间转换时它们的值相等,则它们是相等的;对于字符串类型,具有相同内容的未加引号和带引号的字符串被认为是相等的;对于颜色类型,具有相同的红色、绿色、蓝色和alpha值,则颜色是相等的。

颜色运算

很少用

body {
  color: #020304 + #050203;
}

转换后的CSS:

body {
  color: #070507;
}

其实是分段计算的,也就是 02 + 05 、03 + 02 、04 + 03

字符串运算

在 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;
}

布尔运算

很少用

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

转换后的CSS:

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

实战应用:
我们在做 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);
  }
}

转换后的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 这个变量抽出来到专门维护变量的文件中,然后供项目中所有的页面做运算使用。

小结:
Sass基础入门_第8张图片

Sass 插值

插值指在特定的区域插入一段表达式或者一个变量,以此来实现内容动态变换的需求, 语法:#{$name} ,其中$name是一个变量。其实有点像 ES6的字符串模板功能。

在选择器名字中使用插值
$name: item;
.ul-#{$name} { // 使用插值
  width: 200px;
  .li-#{$name} { // 使用插值
    width: 100%;
  }
}
.box-#{$name} { // 使用插值
  height:100px;
  .#{$name} { // 使用插值
    height:100%;
  }
}

转换后的CSS:

.ul-item {
  width: 200px;
}
.ul-item .li-item {
  width: 100%;
}

.box-item {
  height: 100px;
}
.box-item .item {
  height: 100%;
}

这样改名字的时候就方便了,只需要修改插值的名字,不用一个一个的去改了。。。

在属性名中使用插值
$name: color;
$position: top;
body {
  background-#{$name}: red;
  border-#{$name}: blue;
  padding-#{$position}: 5px;
  margin-#{$position}: 10px;
  #{$position}: 20px;
}

转换后的CSS:

body {
  background-color: red;
  border-color: blue;
  padding-top: 5px;
  margin-top: 10px;
  top: 20px;
}

在属性值中使用插值
$one: 20px;
$two: 2;
$family: "UaTy";
div {
  margin: $one / $two; // 除法运算
  margin: #{$one} / #{$two}; // 分隔
  font-family: "MyFo #{$family}"; // 带引号的字符串会转换为不带引号
  width: calc(100% - $one * 2 *$two); // calc函数中内容会被当作字符串处理
  width: calc(100% - #{$one * 2 *$two}); // calc函数中插值的内容会进行运算
}

转换后的CSS:

div {
  margin: 10px;
  margin: 20px/2;
  font-family: "MyFo UaTy";
  background-image: url(http://xxx.xxx.xxx/a.jpg);
  width: calc(100% - $one * 2 *$two);
  width: calc(100% - 80px);
}

需要注意的是,我们对两个变量使用 / 标识符的时候,如果你不想对这两个变量进行除法运算而是进行分隔,那么就可以使用插值避免运算。

在注释中使用

不举例了,谁会在注释中使用这个。。。想不通

小结:
Sass基础入门_第9张图片

Sass 函数

我们先来看一段 Sass 函数的代码来直观感受一下:

$list: [1,2,4,5];
$string: 'string';
$substring: 'str';

.box {
  font-size:length($list); // 列表函数
  font: quote($string); // 字符串函数
  font: str-index($string, $substring); // 字符串函数
  color: adjust-hue(#6b717f, 60deg); // 颜色函数
  border-width: ceil(4.2); // 数字函数
  width: percentage(.7); // 数字函数
  font: type-of(#676767); // Introspection函数
}

转换后的CSS:

.box {
  font-size: 4;
  font: "string";
  font: 1;
  color: #796b7f;
  border-width: 5;
  width: 70%;
  font: color;
}

在实际开发中并不会这么写,这里把它们作为属性值只是为了直观的展示。

字符串函数
string.quote(aaa) //=> "aaa" 加引号
unquote("bbb")  //=> bbb     去引号
str-index("abcde", "a") //=> 1 查找字符索引
str-index("abcde", "c") //=> 3
str-insert("abcde", "j", 1)  //=> "jabcde" 指定位置插入字符串
str-insert("abcde", "j", 4)  //=> "abcjde"
str-insert("abcde", "j", 100)  //=> "abcdej"
str-insert("abcde", "j", -20)  //=> "jabcde"
str-length("abcde")  //=> 5 获取字符串长度
str-slice("abcde", 1, 2)  //=> "ab" 从指定位置开始截取指定长度的字符串
str-slice("abcde", 2, 4)  //=> "bcd"
to-upper-case("abcde") //=> "ABCDE"  转为大写
to-upper-case("Abc")  //=> "ABC"  转为大写
to-lower-case("ABC")  //=> "abc"  转为小写
to-lower-case("Abc")  //=> "abc"  转为小写
unique-id()  //=> urgdjis 生成唯一字符串标识
Math 函数
math.$e  //=> 2.7182818285  返回数学常数 e 的值
math.$pi //=> 3.1415926536  返回数学常数 π 的值
ceil(4.2)  //=> 5  向上取整
floor(4.8)  //=> 4  向下取整
round(4.3)  //=> 4  四舍五入取近似值
round(4.7)  //=> 5  四舍五入取近似值
abs(-10px)  //=> 10px  取绝对值
math.cos(100deg)  //=> -0.1736481777 返回余弦值,单位必须与deg兼容或无单位
math.sin(100deg) //=> 0.984807753  返回正弦值,单位必须与deg兼容或无单位
math.tan(100deg) //=> -5.6712818196  返回正切值,单位必须与deg兼容或无单位
math.acos(0.5) //=> 60deg  返回反余弦值,传入的参数不可带单位
math.asin(0.5) //=> 30deg  返回反正弦值,传入的参数不可带单位
math.atan(10) //=> 84.2894068625deg  返回反正切值,传入的参数不可带单位
math.log(10)  //=> 2.302585093 对数函数
math.log(10, 10)  //=> 1
math.pow(10, 2)  //=> 100 幂运算
math.sqrt(100)  //=> 10 求平方根
math.max(8, 4)  //=>  8 返回二者的最大的值
math.min(8, 4)  //=>  4 返回二者的最小的值
ndom()  //=> 返回一个 0~1 之间的随机数
percentage(0.2)  //=> 20%  将无单位的小数转换为百分比数
comparable(10px, 10)  //=> true  判断两个数值的单位是否兼容
comparable(10px, 10px)  //=> true
comparable(10px, 10em)  //=> false
unitless(100)  //=> true  判断传入的数值是否没有单位
unitless(100px)  //=> false
unit(8)  //=> ""
unit(8px)  //=> "px" 返回传入数值的单位
unit(8em)  //=> "em"
列表函数
append(10 11 12, 13)  //=> 10 11 12 13  向一个列表的末尾插入元素
append((10,11,12), 13)  //=> 10, 11, 12, 13

join(5 6, 7 8)  //=> 5 6 7 8  连接两个列表
join((5,6), (7,8))  //=> 5, 6, 7, 8  
join(5 6, 7 8, $bracketed: true)  //=> [5 6 7 8]

index(a b solid, b)  //=> 2 返回 $value 在列表 $list 中的索引
index(a b solid, solid)  //=> 3

length(a b solid)  //=> 3 返回列表的长度
length("")  //=> 1

list-separator(a b)  //=> space
list-separator((a,b))  //=> comma 返回列表的分隔符

nth(a b c d, 2)  //=> b 通过索引在列表中取元素
条件函数
if(true, 18px, 16px)  //=> 18px  三目运算
if(true, 18px, 16px)  //=> 16px

Map 函数
$val_map: ("a": 1, "b": 2, "c": 3); // 定义 maps 类型的数据
map-get($val_map, "a")  //=> 1
map-get($val_map, "b")  //=> 2

$val_map: ("a": 1, "b": 2, "c": 3); // 定义 maps 类型的数据
map-has-key($val_map, "b")  //=> true
map-has-key($val_map, "e")  //=> false

$val_map: ("a": 1, "b": 2, "c": 3); // 定义 maps 类型的数据
map-keys($val_map)  //=> "a","b","c"

$val_map1: ("a": 1, "b": 2);
$val_map2: ("c": 3, "d": 4);
map-merge($val_map1, $val_map2)
// => 返回的数据
// (
//   "a": 1,
//   "b": 2,
//   "c": 3,
//   "d": 4
// )

$val_map: ("a": 1, "b": 2, "c": 3); // 定义 maps 类型的数据
map-remove($val_map, "a", "b")  //=> ("c": 3)

$val_map: ("a": 1, "b": 2, "c": 3); // 定义 maps 类型的数据
map-values($val_map)  //=> 1,2,3

颜色函数
blue(#BA55D3)  //=> 211 获取RGB通道色值
red(#BA55D3)  //=> 186
green(#BA55D3)  //=> 85

saturate(#BA55D3, 20%)  //=> #c740e8 调整饱和度

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)  调整不透明度

实战应用:
一般来说我们的项目中会有一个 function.scss 文件来单独维护各种各样的函数

// 截取字符串的后半部分
@function middleStr($str) {
  $leng: str-length($str);
  $start: $leng / 2;
  @return str-slice($str, $start, $leng);
}

// 判断class长度范围
@function classLong($class, $max) {
  $leng: str-length($class);
  @if $leng > $max {
    @return true;
  } @else {
    @return false;
  }
}

// 大小写转换
@function upperOrLower($str, $type) {
  @if type-of($str) == "string" {
    @if $type == "upper" {
      @return to-upper-case($str);
    } @else {
      @return to-lower-case($str)
    }
  }
}

Sass中的 @-Rules 指令

什么是 @-Rules 呢?其实就是以 @ 开头的一些规则,在 CSS 中有很多 @-Rules 是你经常用的,比如 @media 、@font-face 等等;在 Sass 中除了支持 CSS 所有的 @-Rules 外,Sass 还提供了一些扩展的 @ 规则。

@use 和 @import

用于导入sass文件,官方建议使用 @use 来替代 @import

@forward

也用于导入sass文件,主要用于跨文件导入时中间转发

我在 src/a.scss 中定义了一个 mixin:

// src/a.scss 
@mixin bor {
	width: 100px;
	height: 100px;
}

然后在 b.scss 中使用 @forward 来导入它:

// b.scss
@forward 'src/a';

现在,我是想在和 b.scss 同级的 c.scss 文件中使用 a.scss 中的 mixin,那么在 c.scss 中我们可以不直接导入 a.scss,我们通过导入 b.scss 也可以实现:

// c.scss
@use "b";
li {
	// 应用 a.scss 文件中的 mixin
	@include b.bor;
}
@mixin 和 @include

@mixin 是混合器指令,@include 是用来引用混合指令的

@function

函数指令,用来定义函数

@extend

继承指令

@error、@warn、@debug

调试信息相关,@error@warn差不多,都可以打印信息,区别是 @error 会停止函数运行,而@warn不会停止函数运行,只打印信息。@debug 来打印表达式的值以及文件名和行号等信息。

@function my($str) {
	@if $str != 'a' || $str != 'b'  {
		// 此处会抛出错误信息并停止函数的运行
		@error "This is a error!"
	}
}
@function my($str) {
	@if $str != 'a' || $str != 'b'  {
		// 此处不会停止函数的运行
		@warn "This is a message!"
	}
}
@at-root

用来取消嵌套规则

.a {
  width: 300px;
  .b {
    width: 200px;
  }
  .c {
    width: 100px;
    // 取消嵌套规则
    @at-root .f {
      width: 20px;
    }
  }
  // 取消嵌套规则
  @at-root .e {
    width: 50px;
  }
}

转换后的CSS:

.a {
  width: 300px;
}
.a .b {
  width: 200px;
}
.a .c {
  width: 100px;
}
.f {
  width: 20px;
}

.e {
  width: 50px;
}

注意其中的 .f 和 .e 并没有应用嵌套规则

控制规则

包括 @if 、@each、@for 和 @while等,后面会介绍

小结:
Sass基础入门_第10张图片

Sass 控制指令

@if、@else 和 @else if 指令

@if 指令格式为: @if 表达式 { … } , 在 @if 后跟一个表达式,如果表达式为 true 则执行 {} 里的代码逻辑,@else@else if 以此类推,功能跟原生开发语言一样。

@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); }

转换后的CSS:

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

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

.c {
  height: 100px;
  width: 100px;
}
@each 指令

@each 指令的语法为: @each $variable in expression { … } ,一般用来循环一个列表或 Map ,其中 expression 是一个列表或者返回一个列表的表达式,$variable 是列表中的每一项,{} 中是每次循环都会执行的代码.

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

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

转换后的CSS:

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

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

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

这样生成不同粗细大小的边框样式是不是方便了很多

@for 指令

for循环指令,语法是:
@for $variable from [start] to [end] { … }
或者
@for $variable from [start] through [end] { … }
其中variable 都是每次循环时的索引变量,start 都表示开始的边界,end 都表示结束的边界;二者区别是 through 包含 starten ,而 to 包含 start 但不包含 end

through的写法:

$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%);
  }
}

转换后的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%);
  }
}

转换后的CSS:

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

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

to是包前不包后的,跟正常的for循环写法一样

@while 指令

@while 指令的语法 @while expression { … } ,当表达式 expression 结果为 true 时就执行 {} 里的代码,直到 false 时跳出循环。

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

转换后的CSS:

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

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

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

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

这个也很方便

实战应用:
需求将视口分为 12 等份,然后根据不同的 class 类名来为其宽度设置不同的百分比:

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

这样就能一下子得到 .width-0 到 .width-12 的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%;
}

小结:
Sass基础入门_第11张图片

Sass 函数指令

语法:@function name(arguments…){}@function 是定义函数的指令,name 是函数名,arguments 是参数列表,也可以没有参数。其中函数名将连字符和下划线视为相同,也就是说 a_ba-b 是同一个函数。

函数定义

使用@function 定义函数

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

函数只要定义了参数就必须传入这些参数,但是你可以定义默认值使参数成为可选的

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

转换后的CSS:

.p {
  font: 1;
  font: 4;
}
接收任意数量的参数

函数指令将最后一个参数以 … 结尾也可以接收任意数量的参数

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

转换后的CSS:

.p {
  font: "one", "two", "three";
}
@return 指令

@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");
}

转换后的CSS:

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

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

小结:
Sass基础入门_第12张图片

Sass 混合器指令

混合器指令可以定义重复使用的样式,避免编写过多重复的样式代码,它将帮你更合理的维护样式代码。

语法:@mixin name { … } 或者 @mixin name() { … },第一种是不传参的指令,第二种是传参的指令。混合器本身其实是一个函数定义。

定义和引用混合指令

使用 @mixin 定义,而使用 @include 进行引用混合器

// 不接收参数的混合指令
@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);
}

转换后的CSS:

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

.item {
  font-family: "Myfont";
  font-weight: 500;
  font-size: 20px;
}
混合器传参

可以跟函数一样传参数,但是这些参数只要声明了就必须传入,如果你想让某个参数成为可选的,你需要为这个参数赋一个默认值

// 没有赋默认值的参数
@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);
}

有时候 @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 指令除了可以接收参数外,还可以接收样式块,在 @mixin 中可以使用 @content 来声明接收的内容块,内容块是通过 {} 的方式传入的,然后会注入到 @content 所在的位置。

@mixin hover {
  &:hover {
    @content;
  }
}

.button {
  border: 1px solid black;
  @include hover {
    border-width: 2px;
  }
}

转换成的CSS代码:

.button {
  border: 1px solid black;
}
.button:hover {
  border-width: 2px;
}

实战应用:
在项目中建立专门的 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");
  }
}

小结:
Sass基础入门_第13张图片

Sass 继承

语法:@extend selector 也就是在 @extend 后面跟一个选择器,表示继承这个选择器的样式。

.a {
  width: 10px;
}

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

转换成的CSS代码:

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

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

注意这里转换后的结果,.a, .b 与前面介绍的 Sass嵌套生成的结果 .a .b 的区别多了个逗号,前者表示所有选择器为.a的标签和所有选择器为.b的标签,同时具备该样式,后者表示后代选择器,即选中.a标签里面的.b的标签。

复杂的例子:

.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;
}

转换成的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 就可以为你自动创建这些组合,提取相同的样式,所以如果有选择器使用了相同的样式,请使用继承的方式来实现!

占位符选择器

Sass中的选择器除了有CSS的 idclass 选择器以外,还有 以 % 开头的占位符选择器,但是在 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;
}

转换成的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;
}
在 @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;
  }
}

实战应用:

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

小结:
Sass基础入门_第14张图片

Sass 导入

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 的文件。

加载路径

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

@import 'a';

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

部分导入

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

@import 'my';

另外需要注意的是:不可以同时存在带有下划线和不带下划线的同名文件!

索引文件

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

@import 'my'

那么这样导入的就是 _index.scss 文件,也就是说 _index.scss 是这个目录下的默认文件,跟我们导入目录下的index.html index.js是一样的道理。

导入 CSS

Sass 也可以直接导入一个 CSS 文件,要注意的是,在导入的 CSS 文件中不允许有任何 Sass 的特性和语法,如果有的话将会报错!导入的 CSS 文件会按照原有的样子呈现。

嵌套导入

Sass 允许在样式表中嵌入 @import,使用这种方式的话,以下划线开头的文件内容将会被直接插入到使用 @import 的位置

// _a.scss
. item {
	width: 100px;
	height: 200px;
}

在 style.scss 中导入上面的 _a.scss 文件:

// style.scss
.box {
	@import '_a.scss';
}

转换成的CSS代码:

.box {
	.item {
		width: 100px;
		height: 200px;
	}
}

这种用法很少用。

使用 @use 代替 @import

Sass 官方团队不鼓励使用 @import 导入,并且在未来几年将逐步淘汰它,并最终将 @import 从 Sass 中完全删除。官方推荐@use替代@import。二者语法是一样的:

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

官方想淘汰@import的原因之一是它导入的变量、函数等都是全局可以访问的,开发者很难去维护这些定义的东西,如可能造成命名冲突等。

实战应用:
在实际项目中多数还是使用 @import 来进行导入文件:
Sass基础入门_第15张图片

Sass 输出格式

在 Sass 中有 4 种输出格式:

  • 嵌套格式(:nested)
  • 展开格式(:expanded)
  • 紧凑格式(:compact)
  • 压缩格式(:compressed)

嵌套格式:

.box {
	width: 100px; }
.box2 {
	width: 200px;
	height: 100px;  }
	.box2 p {
		color: red; }

展开格式:

.box {
  width: 100px;
}
.box2 {
  width: 200px;
  height: 100px;
}
.box2 p {
  color: red;
}
.box2 .main {
  width: 100%;
  heigth: auto;
}

紧凑格式:

.box { width: 100px; }

.box2 { width: 200px; height: 100px; }
.box2 p { color: red; }
.box2 .main { width: 100%;heigth: auto; }

压缩格式:

.box{width:100px;}.box2{width:200px;height:100px;}.box2 p{color:red;}.box2 .main{width:100%;heigth:auto;}

一般线上生成环境使用的是压缩格式。

输出格式的设置:

在前端项目里使用 Sass 一般都会安装 node-sass 这个依赖包,node-sass 的设置选项中有一个 outputStyle 可以用于设置输出格式,示例如下:

var sass = require('node-sass');
sass.render({
  file: '/path/to/myFile.scss',
  includePaths: [ 'lib/', 'mod/' ],
  // 设置输出格式
  outputStyle: 'compressed'
}, function(error, result) {
    console.log(error.status);
  }
});

在前端的项目中我们一般会使用 webpack 作为构建工具,所以我们可以在 sass-loader 中来设置 sass 的输出格式:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              sassOptions: {
                outputStyle: 'compressed'
              },
            },
          },
        ],
      },
    ],
  },
};

Sass 注释

可以像java代码中那样加注释,单行注释使用 // 多行注释使用 /**/, Sass的注释是不会被编译到CSS中的,所以可以放心大胆的写。

// 单行注释
.box1{
	width:100px
}
/*
* 多行注释
* 多行注释
* /
.box2{
	width:100px
}

Less又是什么?

除了.scss后缀文件还经常看的.less后缀的文件,Less 是什么呢?跟Sass是什么关系?

Less 也是一种css预处理器,Sass 是对css的扩展,Less可以理解为Sass的简化版本, 总体使用体验差不多,但是二者实现方式不同。Less 既可以在客户端上运行 (支持IE 6+, Webkit, Firefox),也可在服务端运行 (借助 Node.js)。

Sass 与 Less 的区别
  1. 编译环境不一样

    Sass是在服务端处理的,以前是Ruby,现在是Dart-SassNode-Sass,而Less是需要引入less.js来处理Less代码输出CSS到浏览器,也可以在开发环节使用 Less,然后编译成 css 文件,直接放到项目中,也有 Less.app、SimpleLess、CodeKit.app 这样的工具,也有在线编译地址。

  2. 输出设置不同Less 没有输出设置,Sass 提供4种输出设置

  3. Less 不支持条件语句,Sass 支持条件语句Sass 可以使用if{} else{}, for{}循环等等。

  4. 定义变量方式不同Less@Sass$

  5. Sass 和 Less 的工具库不同

    Sass有工具库Compass, 简单说,Sass和Compass的关系有点像Javascript和jQuery的关系,Compass是Sass的工具库。在它的基础上,封装了一系列有用的模块和模板,补充强化了Sass的功能。
    Less有UI组件库Bootstrap,Bootstrap是web前端开发中一个比较有名的前端UI组件库,Bootstrap的样式文件部分源码就是采用Less语法编写。

Sass 与 Less 的相同之处

Less和Sass在下面的使用上是相同的:

  1. 混入(Mixins)——class中的class;
  2. 参数混入——可以传递参数的class,就像函数一样;
  3. 嵌套规则——Class中嵌套class,从而减少重复的代码;
  4. 运算——CSS中用上数学;
  5. 颜色功能——可以编辑颜色;
  6. 名字空间(namespace)——分组样式,从而可以被调用;
  7. 作用域——局部修改样式;
  8. JavaScript 赋值——在CSS中使用JavaScript表达式赋值。

所以掌握了Sass的语法,学习Less基本上是不费力的,因为使用上几乎一样

Sass官网:https://sass-lang.com/
Less官网:https://less.bootcss.com/

关于 Dart Sass

虽然很多前端项目还是在使用 node-sass,但是由于node-sass底层依赖 libsass,导致很多用户下载安装特别困难,尤其是 windows 用户,还要必须安装python2Visual Studio才能编译成功。所以官方已经主推将node-sass迁移到 dart-sass,并且 新特性将会 dart-sass 上面实现

详细请看:

  • Dart Sass
  • Node Sass to Dart Sass

按照官方的说法,Dart Sass 主要是用来运行在 Dart-VM 上,运行速度很快,也就是你需要安装 Dart SDK, 使用Dart语言来开发前端项目,显然这目前还不是国内开发者的首选。但是官方说 Dart Sass 也可以被编译为纯 JavaScript 并在 npm 上作为 sass 软件包发布使用,但是我感觉应该还是不会特别快,毕竟不是Dart运行环境。


参考:http://www.imooc.com/wiki/sasslesson

你可能感兴趣的:(React,sass,scss,less,node-sass,dart-sass)