头图

【Sass/SCSS 官方英文文档翻译整理】SCSS 完整自学中文版教程01_Sass基本介绍

drylint

Sass 基本介绍

[toc]

如果对本文有任何问题,建议,或者在前端技术体系方面有任何问题,可以添加我的微信: drylint , 我会尽可能为你解答,也会拉你进入前端技术进阶交流群,大家一起进步~

Sass 是 CSS 的超集,支持所有 css 语法,并在其基础上扩展。

Sass 支持像 css 一样的大括号语法,文件扩展名为 .scss ,以及另一种使用缩进的语法,文件扩展名为 .sass

教程主要采取完全兼容 css 的 SCSS 语法。

注释(Comments)

支持两种注释,分别是:

  • 单行注释 // 注释文字
  • 多行注释 /* 注释文字 */

单行注释(Single-line comments)

编译的时候会直接被忽略,不会编译到 CSS 中,所以也叫做“隐式注释”(silent comments)。

// 注释内容

多行注释(Multi-line comments)

编译时会将注释编译到 css 中,所以也叫做“显式注释”(loud comment)

// 这一行注释不会出现在编译的 css 中

/* 这一行会出现在编译的 css 中,除非是在压缩模式下则不会 */

/* 注释中还可以包含插值:
 * 1 + 1 = #{1 + 1} */

/*! 这行注释即使在压缩模式下也会编译到 css 中 */

p /* 多行注释可以写在任何
   * 允许空白出现的地方 */ .sans {
  font-size: 16px;
}

编译后的 css:

/* 这一行会出现在编译的 css 中,除非是在压缩模式下则不会 */

/* 注释中还可以包含插值:
 * 1 + 1 = 2 */

/*! 这行注释即使在压缩模式下也会编译到 css 中 */
p .sans {
  font-size: 16px;
}

SassDoc

文档注释,类似于 jsdoc 。使用三斜线 /// 声明。

/// Computes an exponent.
///
/// @param {number} $base
///   The number to multiply by itself.
/// @param {integer (unitless)} $exponent
///   The number of `$base`s to multiply together.
/// @return {number} `$base` to the power of `$exponent`.
@function pow($base, $exponent) {
  $result: 1;

  @for $_ from 1 through $exponent {
    $result: $result * $base;
  }

  @return $result;
}

特殊的函数(Special Functions)

  • url()
  • xxx

url()

url() 函数在CSS中很常用,但是它的语法与其他函数不同,它可以接受带引号的 url ,也可以接受不带引号的 url。因为未加引号的 URL 不是有效的 SassScript 表达式,所以 Sass 需要特殊的逻辑来解析它。

如果 url() 的参数是一个有效的无引号的 url ,Sass 会原样解析它,当然,插值也是可以用的。

如果参数不是一个有效的无引用的 url ,例如,如果它包含变量或函数调用,它将被解析为普通的 CSS 函数调用。

$roboto-font-path: "../fonts/roboto";

@font-face {
  // This is parsed as a normal function call that takes a quoted string.
  src: url("#{$roboto-font-path}/Roboto-Thin.woff2") format("woff2");
}

@font-face {
  // This is parsed as a normal function call that takes an arithmetic
  // expression.
  src: url($roboto-font-path + "/Roboto-Light.woff2") format("woff2");
}

@font-face {
  // This is parsed as an interpolated special function.
  src: url(#{$roboto-font-path}/Roboto-Regular.woff2) format("woff2");
}

编译后的 css :

@font-face {
  src: url("../fonts/roboto/Roboto-Thin.woff2") format("woff2");
}

@font-face {
  src: url("../fonts/roboto/Roboto-Light.woff2") format("woff2");
}

@font-face {
  src: url(../fonts/roboto/Roboto-Regular.woff2) format("woff2");
}

calc()element()

calc()element() 函数是在 CSS 规范中定义的。因为 calc() 的数学表达式与 Sass 的算法冲突,而 element() 的id可以被解析为颜色,所以它们需要特殊的解析。

Sass 允许任何文本出现在这些函数调用中,包括嵌套的圆括号。

除了可以使用插值来注入动态值会被编译处理。其他任何东西都不会被解释为 SassScript 表达式进行计算,而是原样输出。

progid:...()expression() 弃用

expression() 和以 progid: 开头的函数是使用非标准语法的 Internet Explorer 遗留特性。尽管最近的浏览器已经不再支持它们,但是 Sass 继续解析它们以实现向后兼容。

min()max()

CSS在 CSS Values and Units Level 4 中增加了对 min()max() 函数的支持,Safari 很快就采用了它们来支持 iPhoneX 。

但是 Sass 在很久以前就已经有了自己的 min()max() 函数,为了向后兼容所有现有的样式表。这就需要额外的句法技巧来实现。

如果一个 min()max() 函数调用是有效的纯 CSS ,它将被编译为普通的 CSS 的 min()max() 函数调用。

"纯CSS "包括嵌套调用 calc()env()var()min() ,或 max() ,以及插值。

但是,只要调用的时候包含了 SassScript 特性(如变量或函数调用),它就会被认为是对 Sass 自带的 min()max() 函数的调用。

变量

在 Sass 中,声明变量必须以 $ 开头。

$red: #f00;

div {
  color: $red;
}

编译后的 css :

div {
  color: #f00;
}

Sass 变量和 css 变量的区别:

  • Sass 变量会被编译成真实的值然后输出为 css ,也就是仅仅存在于开发阶段。
  • CSS 变量对于不同的元素可以有不同的值,但是 Sass 变量一次只有一个值。
  • Sass 变量是不可逆的,这意味着如果您使用了一个变量,然后在后面更改了它的值,那么之前的使用将依然保持不变。CSS 变量是声明性的,这意味着如果在后面更改了值,它将影响前面的使用和以后的使用。

注意:和所有的 Sass 标识符一样,Sass 变量将连字符 - 和下划线 _ 视为相同的字符。这意味着 $font-size$font_size 都指向同一个变量。这是 Sass 早期的历史遗留,当时它只允许在标识符名称中使用下划线。后来, Sass 增加了对连字符的支持,以匹配 CSS 的语法,sass 将这两个字符视为等效处理,以便于使迁移更加容易。

默认值

比如开发一个库,用户可以选择是否传递自定义的值,如果没有传递则使用默认值。

为了实现这一点,Sass 提供了 !default 标志。只有当变量没有定义或者它的值为 null 时,才会给该变量赋值。否则,将使用默认的值。

配置模块变量

!default 定义的变量,可以在使用 @use 规则加载模块时配置。

在模块中声明变量,并定义默认值:

// _library.scss

$black: #000 !default;
$border-radius: 0.25rem !default;
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;

code {
  border-radius: $border-radius;
  box-shadow: $box-shadow;
}

在引用模块时,选择要自定义值的变量,忽略的变量则使用默认值:

// index.scss

@use 'library' with (
  $black: #222,
  $border-radius: 0.1rem
);

内置变量

内置模块定义的变量是无法被修改的。

比如,下面代码视图修改内置变量,但不会成功:

@use "sass:math" as math;

// This assignment will fail.
math.$pi: 0;

作用域

在 css 文件顶层声明的变量是全局变量,声明后可以在模块中的任何地方被访问。

在块({})中声明的变量是局部变量,只能在声明它们的块内访问。

// 全局变量
$red: #f00;

div {
  // 局部变量
  $black: #000;

  color: $red;
}

p {
  // 在这里引用局部变量编译时会报错
  color: $black;
}

当局部变量和全局变量重名时,不会覆盖全局变量,而是同时存在,在哪个作用域访问的就是哪个变量。

$red: #f00;

div {
  $red: #f55;

  color: $red;
}

p {
  color: $red;
}

编译后的 css :

div {
  color: #f55;
}

p {
  color: #f00;
}

如果想用一个局部变量去覆盖全局变量,也就是在块中修改全局变量的值,可以使用 !global 来完成:

$red: #f00;

div {
  // !global 将修改全局变量的值,而不是在块中新建一个局部作用域
  $red: #f55 !global;

  color: $red;
}

p {
  color: $red;
}
div {
  color: #f55;
}

p {
  color: #f55;
}

注意:如果使用 !global 的变量不是一个全局变量,则编译时会报错。

在流程控制语句(@if/@each/@for/@while 等)中声明的变量有一个自己的特殊作用域,它不会创建新变量去覆盖同级作用域中的同名变量,而是简单地进行原变量的赋值修改操作。

$dark-theme: true;
$red: #e55;
$black: #333;

@if $dark-theme {
  $red: #f00;
  $black: #000;
}

.button {
  background-color: $red;
  color: $black;
}

编译后的 css :

.button {
  background-color: #f00;
  color: #000;
}

在流程控制语句中,赋值给已经存在的变量则是修改操作,如果是不存在的变量则会创建一个新的变量,但这个新的变量也只能在这个流程控制语句的作用域中使用。

检测变量是否存在

Sass 核心库提供了两个用于处理变量的高级函数。meta.variable-exists() 函数返回给定名称的变量是否在当前作用域中存在, meta.global-variable-exists() 函数做同样的事情,但仅用于全局作用域。

@debug meta.variable-exists("var1"); // false

$var1: value;

@debug meta.variable-exists("var1"); // true

h1 {
  // $var2 is local.
  $var2: value;

  @debug meta.variable-exists("var2"); // true
}
@debug meta.global-variable-exists("var1"); // false

$var1: value;

@debug meta.global-variable-exists("var1"); // true

h1 {
  // $var2 is local.
  $var2: value;

  @debug meta.global-variable-exists("var2"); // false
}

用户有时可能会希望使用插值来定义基于另一个变量的变量名。Sass 不允许这样做,因为它使得我们很难一眼就知道哪些变量在哪里定义。但是,您可以做的是定义一个从名称到值的 map,然后您可以使用变量访问该映射。

@use "sass:map";

$theme-colors: (
  "success": #28a745,
  "info": #17a2b8,
  "warning": #ffc107,
);

.alert {
  // Instead of $theme-color-#{warning}
  background-color: map.get($theme-colors, "warning");
}

编译后的 css :

.alert {
  background-color: #ffc107;
}

插值(Interpolation)

插值几乎可以在 Sass 样式表的任何地方使用,以将 SassScript 表达式的结果嵌入到 CSS 块中。

#{} 中放置一个表达式即可,比如可以用在:

  • 选择器
  • 属性名
  • 自定义属性值
  • CSS 的 @ 语句中
  • @extends
  • CSS @imports
  • 字符串
  • 特殊函数
  • CSS 函数名
  • 保留注释(Loud comments ) /* ... */

下面展示部分用法,在选择器,属性,继承,注释语句中使用插值:

$selector: "hello";
$color: "color";

/* selector: #{$selector} */
.#{$selector} {
  background-#{$color}: #f00;
}

.#{$selector}-2 {
  @extend .#{$selector};

  border-#{$color}: #f00;
}
/* selector: hello */
.hello,
.hello-2 {
  background-color: #f00;
}

.hello-2 {
  border-color: #f00;
}

在 SassScript 中,可以使用插值表达式将 SassScript 注入到未加引号的字符串中。这在动态生成名称(例如动画)或使用斜杠分隔值时特别有用。

注意: SassScript 中的插值永远返回一个未加引号的字符串,在上面的例子中已经看到了。

插值对于将值注入到字符串中很有用,但除此之外,在 SassScript 表达式中很少需要插值。

比如,使用变量完全不需要这样写: color: #{$red} ,而是可以直接使用变量: color: $red

注意:不应该使用插值插入数字。因为插值总是返回未加引号的字符串,返回值并不能进一步用于计算,这也同时避免了违反 Sass 内置的的安全保护规则,以确保能够正确使用单位。

Sass 有强大的单位运算,你可以使用它来代替。例如,与其写 #{$width}px ,不如写 $width * 1px,或者更好的是,以px开头声明$width变量。这样,$width 已经有单位,你将得到一个很好的错误消息,而不是编译伪造的CSS。

还有,虽然可以使用插值将带引号的字符串转换为不带引号的字符串,但使用 string.unquote() 函数会更清楚。所以应该用 string.unquote($string) 来代替 #{$string}

@语句(At-Rules)

Sass 在 CSS 之上添加了新的 @ 语句 :

  • @mixin@include 用于复用大的块级样式
  • @function 声明自定义函数,用于 SassScript 表达式中
  • @extend 用于在一个选择器中继承另一个选择器的样式
  • @at-root 将代码块内部的样式编译到 css 最外层(相当于顶级作用域)
  • @error 故意使编译失败而中断,并抛出错误信息
  • @warn 抛出一条错误信息但不使编译程序失败而中断
  • @debug 抛出一条用于 debug 调试的消息
  • @if, @each, @for, @while 流程控制语句

@mixin and @include

@mixin 用于定义要复用的样式块,@include 用于调用这些样式块。

因历史遗留原因,mixin 的名字和 Sass 标识符一样,连字符(hyphens) - 和下划线(underscores)_ 被视为完全相同。

定义 mixin 的语法:

// 不需要传参数时,复用固定的样式代码
@mixin <name> {
  // ...
}

// 或

// 需要使用时传递参数,动态复用样式代码
@mixin name(arg1, arg2, ..., argN) {
  // ...
}

使用 mixin 的语法:

@include <name>;

// 或

@include <name>(arg1, arg2, ...)

使用示例:

// a.scss

@mixin input {
  padding: 10px;
  color: #333;
}

@mixin button ($color, $fontSize) {
  color: $color;
  font-size: $fontSize;
}
@use "a";

.input {
  @include a.input;
}

.button {
  @include a.button(#333, 20px);
}

编译后的 css :

.input {
  padding: 10px;
  color: #333;
}

.button {
  color: #333;
  font-size: 20px;
}

通常情况下,如果一个 mixin 定义时有多少个参数,那么在调用时必须传递相同数量的参数,除非是定义 mixin 时使用了参数默认值。

mixin 参数默认值

定义一个参数默认值就像定义一个变量一样,参数名后加一个冒号,然后就可以写默认值了。

@mixin button ($color, $fontSize: 16px) {
  color: $color;
  font-size: $fontSize;
}

.button {
  @include button(#f00);
}

编译后的 css :

.button {
  color: #f00;
  font-size: 16px;
}

默认参数值可以是任意 Sass 表达式,甚至是它前面的参数。

@mixin font ($size, $weight: if($size >= 24px, 600, 400)) {
  font-size: $size;
  font-weight: $weight;
}

.div1 {
  @include font(16px);
}

.div2 {
  @include font(24px);
}

编译后的 css :

.div1 {
  font-size: 16px;
  font-weight: 400;
}

.div2 {
  font-size: 24px;
  font-weight: 600;
}

关键词传参

默认情况下,调用 mixin 时传递的参数顺序必须和定义时的参数一一对应。

如果传递参数时指定参数关键词,则可以不按照定义的顺序来传参。

@mixin font ($weight, $size) {
  font-size: $size;
  font-weight: $weight;
}

.div1 {
  @include font($size: 16px, $weight: 500);
}

编译后的 css :

.div1 {
  font-size: 16px;
  font-weight: 500;
}

注意,如果要传递不带关键词的参数,则它必须出现在关键词参数之前。

任意数量的参数

如果 mixin 的最后一个参数名以 ... 结尾,那么这个参数就可以接收传递过来的任意数量的参数,这个参数的值则会是一个列表。

@mixin order($height, $selectors...) {
  @for $i from 0 to length($selectors) {
    #{nth($selectors, $i + 1)} {
      position: absolute;
      height: $height;
      margin-top: $i * $height;
    }
  }
}

@include order(150px, "input.name", "input.address", "input.zip");

编译后的 css :

input.name {
  position: absolute;
  height: 150px;
  margin-top: 0;
}

input.address {
  position: absolute;
  height: 150px;
  margin-top: 150px;
}

input.zip {
  position: absolute;
  height: 150px;
  margin-top: 300px;
}

带有关键字的任意参数

如果调用 mixin 带了关键字,那么任意参数需要使用 meta.keywords() 来处理,处理后将返回一个 map 类型的数据。

如果没有将任意参数传递给 meta.keywords() 函数,那么这个任意参数列表就不允许接收带有关键词的参数,编译程序会报错。

@use "sass:meta";

@mixin syntax-colors($args...) {
  @debug meta.keywords($args);
  // (string: #080, comment: #800, variable: #60b)

  @each $name, $color in meta.keywords($args) {
    pre span.stx-#{$name} {
      color: $color;
    }
  }
}

@include syntax-colors(
  $string: #080,
  $comment: #800,
  $variable: #60b,
)
pre span.stx-string {
  color: #080;
}

pre span.stx-comment {
  color: #800;
}

pre span.stx-variable {
  color: #60b;
}

传递任意参数

接收的任意参数可以是一个列表(list),那么,也可以把一个列表作为任意参数传递,同样只需要在后面加上 ... 即可。

$font: 16px, 600, #f00;

@include font($font...);

同样,也可以把一个 map 作为任意参数传递:

@mixin font ($size, $weight) {
  font-size: $size;
  font-weight: $weight;
}

$font: (
  weight: 600,
  size: 16px,
);

.div1 {
  @include font($font...);
}

编译后的 css :

.div1 {
  font-size: 16px;
  font-weight: 600;
}

@content 样式块

除了接受参数之外,mixin 还可以接受整个样式块,称为内容块。

在 mixin 中,在样式块中写一个 @content 来声明这个位置接受一个内容块,传递一个样式块给 mixin,这个样式块的内容将会用来替换 @content

@mixin font ($size, $weight) {
  font-size: $size;
  font-weight: $weight;
  @content;
}

$font: (
  weight: 600,
  size: 16px,
);

.div1 {
  @include font($font...) {
    font-family: sans-serif;
  }
}

编译后的 css :

.div1 {
  font-size: 16px;
  font-weight: 600;
  font-family: sans-serif;
}

可以书写多个 @content; ,这样将会编译生成多个接收到的样式块。

传递的样式块是有作用域限制的,只能访问样式块所处的位置的变量,而不能去访问 mixin 定义的作用域的变量。

如果要让样式块使用 mixin 定义的作用域的变量,则需要通过 @content() 传递给样式块。

使用 `@content 时传参

传参使用 @content(arg1, arg2, ...) ,接收使用 @include <name> using ($arg1, $arg2, ...)

@mixin font ($size, $weight) {
  font-size: $size;
  font-weight: $weight;
  @content(#f00, $size * 2);
}

$font: (
  weight: 600,
  size: 16px,
);

.div1 {
  @include font($font...) using ($color, $margin) {
    font-family: sans-serif;
    color: $color;
    margin: $margin;
  }
}

编译后的 css :

.div1 {
  font-size: 16px;
  font-weight: 600;
  font-family: sans-serif;
  color: #f00;
  margin: 32px;
}

@content() 同样可以传递 listmap 类型的参数,用法和前面一样。

缩进语法的 mixin

缩进语法的 Sass 可以使用 = 来定义一个mixin,然后使用 + 来使用一个 mixin,但很不直观,不建议使用。

@at-root

通常用于嵌套的选择器中,在选择器前写下 @at-root 语句,用于将该选择器编译到样式表的最外层,而不是嵌套所在的位置。

.div1 {
  color: #f00;
  .div2 {
    color: #0f0;

    // 将 .div3 编译到最外层
    @at-root .div3 {
      color: #00f;
    }
  }
}

编译后的 css :

.div1 {
  color: #f00;
}
.div1 .div2 {
  color: #0f0;
}
.div3 {
  color: #00f;
}

结合 mixin 来使用:

@use "sass:selector";

@mixin unify-parent($child) {
  @at-root #{selector.unify(&, $child)} {
    font-size: 16px;
    @content;
  }
}

.wrapper .field {
  @include unify-parent("input") {
    color: #f00;
  }

  @include unify-parent("select") {
    color: #0f0;
  }
}

编译后的 css :

.wrapper input.field {
  font-size: 16px;
  color: #f00;
}

.wrapper select.field {
  font-size: 16px;
  color: #0f0;
}

@at-root 还有另一种写法 @at-root { ... }

.div1 {
  font-size: 16px;

  @at-root {
    .div2 {
      color: #f00;
    }
    .div3 {
      color: #0f0;
    }
  }
}

编译后的 css :

.div1 {
  font-size: 16px;
}
.div2 {
  color: #f00;
}

.div3 {
  color: #0f0;
}

解决样式之外的东西

默认情况下, @at-root 只会解决普通样式规则, 其他像是 @media@supports 等将会被丢掉。

使用 @at-root (with: <rules...>) { ... }@at-root (without: <rules...>) 来告诉 Sass 在编译的时候是否包括一些指定的规则。

除了合法的 @语句的名称,如 @media 中的 media,还有两个特殊的值可以在查询中使用:

  • rule 指的是样式规则。例如,@at-root (with: rule) 不保留 @ 语句,但保留样式规则。
  • all 指所有 @语句 和 style 规则。
@media screen and (min-width: 900px) {
  .page {
    width: 100px;

    @at-root (with: media) {
      /* @at-root (with: media) */
      .div1 {
        font-size: 16px;
      }
    }

    @at-root (without: media) {
      .div2 {
        /* @at-root (without: media) */
        color: #111;
      }
    }

    @at-root (with: rule) {
      .div3 {
        /* @at-root (with: rule) */
        color: #111;
      }
    }

    @at-root (without: rule) {
      .div4 {
        /* @at-root (without: rule) */
        color: #111;
      }
    }

    @at-root (with: all) {
      .div5 {
        /* @at-root (with: all) */
        color: #111;
      }
    }

    @at-root (without: all) {
      .div6 {
        /* @at-root (without: all) */
        color: #111;
      }
    }
  }
}

编译后的 css :

@media screen and (min-width: 900px) {
  .page {
    width: 100px;
  }

  /* @at-root (with: media) */
  .div1 {
    font-size: 16px;
  }
}
.page .div2 {
  /* @at-root (without: media) */
  color: #111;
}
.page .div3 {
  /* @at-root (with: rule) */
  color: #111;
}

@media screen and (min-width: 900px) {
  .div4 {
    /* @at-root (without: rule) */
    color: #111;
  }
}

@media screen and (min-width: 900px) {
  .page .div5 {
    /* @at-root (with: all) */
    color: #111;
  }
}
.div6 {
  /* @at-root (without: all) */
  color: #111;
}
阅读 117
1 声望
0 粉丝
0 条评论
你知道吗?

1 声望
0 粉丝
宣传栏