2020年的css周边之sass用法指南

sass实现

sass是一个样式表语言,只有语法和定义,需要对应的实现才能根据sass源码转化为css,sass对应于实现就类似于ecma262对应于v8
现在最新的实现是dart-sass,之前还有一个实现是Node-Sass,后者已经废弃,引入dart实现版本的原因请参考Announcing Dart Sass,两种实现的具体区别可以参考Node-Sass or Dart-Sass : The CSS Preprocessor Dilemma

一般我们只需下载sass,其是Dart Sass编译成的纯js版本。下载完就可以直接用sass命令编译sass文件,需要告诉sass在哪个文件输入和输出,并可以添加–watch选项自动编译

sass --watch input.scss output.css
复制代码

也可以指定目录

sass --watch app/sass:public/stylesheets
复制代码

其他用法请参考dart-sass-cli

sass语言规范

语法

sass有两种语法,两种语法特性一致。 一种语法使用.scss扩展名的文件,是css语法的超级,这里的scss之于css就相当于ts之于js。
另一种语法是sass最初的语法,以.sass扩展名,使用缩进代替了第一种的花括号。

样式表结构

就像css一样,大部分sass样式表也是由包含属性声明的样式规则组成,但是sass的样式表同时还有其他。

  1. 声明,不同声明之间用分号分隔
    • 通用声明,可以在样式表的任何地方
      • 变量声明,比如 $var: value
      • 流程控制at-rules
      • @error, @warn, and @debug 规则
    • css声明,这些声明生成css,除了不能用在@function,可以用在任何地方
      • style规则,比如 h1 { /* ... */ }
      • CSS at-rules,比如 @media and @font-face
      • @include 使用Mixin
      • @at-root
    • 顶层声明,用在样式表顶层或者嵌套在顶层的css声明中
      • @use 加载模块
      • @import
      • @mixin 定义mixin
      • @function 定义函数
    • 其他声明
      • 属性声明,比如width: 100px,只能用于style规则和一些css at-rules
      • @extend,用在样式规则
  2. 表达式,一个表达式是在一个属性或变量声明的右侧的任何东西。每个表达式生成一个value,我们将sass的表达式语法称为sassScript。
    • 字面量 最简单的表达式只代表静态值
      • Number,单位可选,比如12100px
      • String,引号可选,比如"Helvetica Neue"bold
      • Colors,可以用16进制表达式或者颜色名,比如#c6538cblue
      • boolean,truefalse
      • null
      • Lists of values,用空格或逗号分隔,可能用方括号包围或者没有括号,比如1.5em 1em 0 2em, Helvetica, Arial, sans-serif[col1-start]
      • Maps,比如("background": red, "foreground": pink)
    • 操作符 数字操作符
      • ==!=用来检查是否相等
      • +, -, *, /, and %
      • <, <=, >, and >=
      • and, or, and not用来进行布尔判断
      • +, -, /也可以用来连接字符串
      • ()用于显式控制操作顺序
    • 其他表达式
      • 变量,比如$var
      • 函数调用,比如nth($list, 1)
      • 特殊函数,比如calc(1px + 100%)
      • 父级选择器,比如&
      • !important

特殊函数

css定义的大部分函数都在scss正常使用,会被解析为一个函数调用,并按原样编译成css.但有一些例外,它们不能仅解析为sassScript表达式.
这里指的是url()等函数,因为在css中的语法和sassScript中不同,需要在sass中特殊处理,这里不展开,具体参考Special Functions

style规则

基本用法和css一样

属性声明

sass中声明的值可以是任何sassScript表达式。

  • 插值,可以用来动态生成任何需要的属性
//scss
@mixin define-emoji($name, $glyph) {
  span.emoji-#{$name} {
    font-family: IconFont;
    font-variant: normal;
    font-weight: normal;
    content: $glyph;
  }
}

@include define-emoji("women-holding-hands", "👭");
复制代码
//css
@charset "UTF-8";
span.emoji-women-holding-hands {
  font-family: IconFont;
  font-variant: normal;
  font-weight: normal;
  content: "👭";
}

复制代码

sass会在插值解析完毕才会解析选择器,所以可以用来插入选择器的任何一部分。

  • 嵌套,可以用于处理父子选择器,和带相同前缀的属性
//scss
.enlarge {
  font-size: 14px;
  transition: {
    property: font-size;
    duration: 4s;
    delay: 2s;
  }

  &:hover { font-size: 36px; }
}
复制代码
//css
.enlarge {
  font-size: 14px;
  transition-property: font-size;
  transition-duration: 4s;
  transition-delay: 2s;
}
.enlarge:hover {
  font-size: 36px;
}

复制代码

注意虽然这个特性很好用,但是很难看出生成多少css,嵌套的越深,就会生成越多css,浏览器就会做更多工作,因此要保证选择器嵌套不要过深。

  • 隐藏声明,如果一个声明的值是null或空字符串,则不会编译到css
  • 自定义属性,css自定义属性,也叫css变量,当sass在编译自定义属性时会把相关字段原样传给css,唯一的个例是插值(#{}),后者可以注入动态的值。
//scss
SCSS SYNTAX
$primary: #81899b;
$accent: #302e24;
$warn: #dfa612;

:root {
  --primary: #{$primary};
  --accent: #{$accent};
  --warn: #{$warn};

  // Even though this looks like a Sass variable, it's valid CSS so it's not
  // evaluated.
  --consumed-by-js: $primary;
}
复制代码
//css
:root {
  --primary: #81899b;
  --accent: #302e24;
  --warn: #dfa612;
  --consumed-by-js: $primary;
}

复制代码

注意插值会移除字符串的引号,如果要保留引号需要使用 meta.inspect()函数

@use "sass:meta";
$string: "dd";
:root {
  --consumed-by-js: #{meta.inspect($string)};
}
复制代码

父选择器

父选择器&用在嵌套的选择器中代表外部的选择器,使得可以用更复杂的方式复用外部的选择器,比如添加伪类。具体使用时会被外部的选择器替换,而不是正常的嵌套行为

//scss
SCSS SYNTAX
.alert {
  // The parent selector can be used to add pseudo-classes to the outer
  // selector.
  &:hover {
    font-weight: bold;
  }

  // It can also be used to style the outer selector in a certain context, such
  // as a body set to use a right-to-left language.
  [dir=rtl] & {
    margin-left: 0;
    margin-right: 10px;
  }

  // You can even use it as an argument to pseudo-class selectors.
  :not(&) {
    opacity: 0.8;
  }
}
复制代码
//css
.alert:hover {
  font-weight: bold;
}
[dir=rtl] .alert {
  margin-left: 0;
  margin-right: 10px;
}
:not(.alert) {
  opacity: 0.8;
}

复制代码

因为父选择器可能是类型选择器,比如h1,因此只能作为复合选择器的开始,比如span&是不允许的,具体参考this GitHub issue.

  • 添加后缀,可以用来给外部的选择器添加后缀,当使用BEM等方法时效果显著,比如&__copy
  • 用于sassScript,返回父选择器,比如判断有没有父选择器
//scss
@mixin app-background($color) {
  #{if(&, '&.app-background', '.app-background')} {
    background-color: $color;
    color: rgba(#fff, 0.75);
  }
}

@include app-background(#036);

.sidebar {
  @include app-background(#c6538c);
}
复制代码
//css
.app-background {
  background-color: #036;
  color: rgba(255, 255, 255, 0.75);
}

.sidebar.app-background {
  background-color: #c6538c;
  color: rgba(255, 255, 255, 0.75);
}

复制代码

还可以把&作为一个表达式传递给函数或者用作插值,当结合selector函数和@at-root规则使用时,可以嵌套出强大的效果。
比如

//scss
@use "sass:selector";

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

.wrapper .field {
  @include unify-parent("input") {
    /* ... */
  }
  @include unify-parent("select") {
    /* ... */
  }
}
复制代码
//css
.wrapper input.field {
  /* ... */
}

.wrapper select.field {
  /* ... */
}

复制代码

占位选择器

占位选择器以%开始,不会输出到css任何东西,但是可以用来extended

//scss
SCSS SYNTAX
%toolbelt {
  box-sizing: border-box;
  border-top: 1px rgba(#000, .12) solid;
  padding: 16px 0;
  width: 100%;

  &:hover { border: 2px rgba(#000, .5) solid; }
}

.action-buttons {
  @extend %toolbelt;
  color: #4285f4;
}

.reset-buttons {
  @extend %toolbelt;
  color: #cddc39;
}
复制代码
//css
.action-buttons, .reset-buttons {
  box-sizing: border-box;
  border-top: 1px rgba(0, 0, 0, 0.12) solid;
  padding: 16px 0;
  width: 100%;
}
.action-buttons:hover, .reset-buttons:hover {
  border: 2px rgba(0, 0, 0, 0.5) solid;
}

.action-buttons {
  color: #4285f4;
}

.reset-buttons {
  color: #cddc39;
}
复制代码

变量

sass的变量名以$开始,可以用变量名代替对应的值。
一个变量声明像一个属性声明: <variable>: <expression>;,但和属性只能在样式规则或at-rule中使用不同,变量可以在任何地方声明。

$base-color: #c6538c;
$border-dark: rgba($base-color, 0.88);

.alert {
  border: 1px solid $border-dark;
}
复制代码

注意css本身也有变量,但是和sass中的表现不一样,区别包括

  1. sass变量会被编译掉,css变量会保留在输出
  2. css变量对于不同元素可以有不通知,但是sass变量同时只能有一个值
  3. sass变量是命令式的,当使用该变量然后改动时,之前使用的变量将不变,css变量是声明式的,如果改变会影响到之前的使用
  • 默认值,可以在变量的值后面添加!default表示默认值,当作为一个模块时,允许用户使用with重写,如果没有标记默认值,则不允许with重写
  • 内置变量,sass内置模块的变量不可改变
@use "sass:math" as math;

// This assignment will fail.
math.$pi: 0;
复制代码
  • 作用域,在顶级作用域声明的变量是全局的,在块(花括号包围的)中定义的是本地的,可以在块中使用!global来设置全局变量,注意只能用于之前在全局声明过的变量。
$variable: first global value;

.content {
  $variable: second global value !global;
  value: $variable;
}

.sidebar {
  value: $variable;
}
复制代码

在控制流中的变量声明只用于分配给外部作用域已经存在的变量以值,不能声明新的

$dark-theme: true !default;
$primary-color: #f8bbd0 !default;
$accent-color: #6a1b9a !default;

@if $dark-theme {
  $primary-color: darken($primary-color, 60%);
  $accent-color: lighten($accent-color, 60%);
}

.button {
  background-color: $primary-color;
  border: 1px solid $accent-color;
  border-radius: 3px;
}
复制代码
  • 高级的变量函数,meta.variable-exists()用于检查当前作用域给定变量是否存在, meta.global-variable-exists()用于检查全局作用域。

注意不能使用插值来基于一个变量定义另一个变量,但是可以使用映射。

@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 at-rules
  • @extends
  • @import
  • 带引号或者不带引号的字符串
  • 特殊函数
  • 一般函数名
  • 注释
//scss
SCSS SYNTAX
@mixin corner-icon($name, $top-or-bottom, $left-or-right) {
  .icon-#{$name} {
    background-image: url("/icons/#{$name}.svg");
    position: absolute;
    #{$top-or-bottom}: 0;
    #{$left-or-right}: 0;
  }
}

@include corner-icon("mail", top, left);
复制代码
//css
.icon-mail {
  background-image: url("/icons/mail.svg");
  position: absolute;
  top: 0;
  left: 0;
}

复制代码

用在sassScript中

可以用来向不带引号的字符串注入一个值,解析后返回不带引号的字符串(正因如此)。但作为属性名时不需要插值,不然#{$accent}会被编译成$accent,如果包含的是带引号的字符串,将去掉字符串后返回。
string.unquote()也可以将带引号的字符串转化为不带引号的字符串。

at-rules

大部分sass另外加的函数都是以at-rules的形式添加的,at-rules指的是以@开头的规则。

@use和@forward

用于导入和转发module和module 成员(mixins, functions, and variables),原有的@import废弃。
所有源文件模块及其属性默认导出,且编译成对应css文件,因此不需要显式导出。如果只想用于导出而不编译,可以在文件名前加下划线_前缀,其中的成员可以添加下划线前缀设为私有。

当导入模块时,会将所有样式规则导入,如果要访问其成员,可默认使用文件名(不带扩展名)作为命名空间,也可以使用as显式指定,如果指定为*,则可以直接使用其公共成员。
另外可以对标记默认值得变量进行重写,语法@use <url> with (<variable>: <value>, <variable>: <value>)

@use "src/corners" as c;
复制代码

当转发模块时,执行转发的文件内不可以使用被转发的内容,可以用于将多个模块统一对外暴露。另外还可以

  • 利用 @forward "<url>" as <prefix>-*为成员增加前缀
  • 利用@forward "<url>" hide <members...> @forward "<url>" show <members...>处理可见性,其中<members...>是逗号分隔的成员。
  • 也可以用with重写默认值

@mixin and @include

分别用函数的方式来创建@mixin name(<arguments...>) { ... }.和使用@include <name>(<arguments...>)可复用的样式规则。

  • 可选参数@mixin replace-text($image, $x: 50%, $y: 50%){}
  • 任意参数,在最后一个参数后面添加...,比如@mixin order($height, $selectors...)
  • 内容块,除了可以通过参数传递数据,还可以通过内容块,即通过@content获取调用时的样式内容
//scss
@mixin hover {
  &:not([disabled]):hover {
    @content;
  }
}

.button {
  border: 1px solid black;
  @include hover {
    border-width: 2px;
  }
}
复制代码
//css
.button {
  border: 1px solid black;
}
.button:not([disabled]):hover {
  border-width: 2px;
}

复制代码

像内容块传递参数,@content也是一个函数,使用using传递参数

//scss
@mixin media($types...) {
  @each $type in $types {
    @media #{$type} {
      @content($type);
    }
  }
}

@include media(screen, print) using ($type) {
  h1 {
    font-size: 40px;
    @if $type == print {
      font-family: Calluna;
    }
  }
}

复制代码
//css
@media screen {
  h1 {
    font-size: 40px;
  }
}
@media print {
  h1 {
    font-size: 40px;
    font-family: Calluna;
  }
}

复制代码

控制流

sass提供了一些控制流用在mixin和function中,包括

  • @if@else,语法分别为@if <expression> { ... }, @else { ... },后者也可以变体为@else if permalink@else if
//scss
SCSS SYNTAX
@mixin triangle($size, $color, $direction) {
  height: 0;
  width: 0;

  border-color: transparent;
  border-style: solid;
  border-width: $size / 2;

  @if $direction == up {
    border-bottom-color: $color;
  } @else if $direction == right {
    border-left-color: $color;
  } @else if $direction == down {
    border-top-color: $color;
  } @else if $direction == left {
    border-right-color: $color;
  } @else {
    @error "Unknown direction #{$direction}.";
  }
}

.next {
  @include triangle(5px, black, right);
}
复制代码
//css
.next {
  height: 0;
  width: 0;
  border-color: transparent;
  border-style: solid;
  border-width: 2.5px;
  border-left-color: black;
}
复制代码
  • @each,用于遍历列表(用逗号分隔的表达式)生成一串样式,语法@each <variable> in <expression> { ... }
//scss
$sizes: 40px, 50px, 80px;

@each $size in $sizes {
  .icon-#{$size} {
    font-size: $size;
    height: $size;
    width: $size;
  }
}

复制代码
//css
.icon-40px {
  font-size: 40px;
  height: 40px;
  width: 40px;
}

.icon-50px {
  font-size: 50px;
  height: 50px;
  width: 50px;
}

.icon-80px {
  font-size: 80px;
  height: 80px;
  width: 80px;
}
复制代码

遍历map@each <variable>, <variable> in <expression> { ... }

$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");

@each $name, $glyph in $icons {
  .icon-#{$name}:before {
    display: inline-block;
    font-family: "Icon Font";
    content: $glyph;
  }
}

复制代码

遍历一个列表的列表@each <variable...> in <expression> { ... }

//scss
$icons:
  "eye" "\f112" 12px,
  "start" "\f12e" 16px,
  "stop" "\f12f" 10px;

@each $name, $glyph, $size in $icons {
  .icon-#{$name}:before {
    display: inline-block;
    font-family: "Icon Font";
    content: $glyph;
    font-size: $size;
  }
}

复制代码
//css
@charset "UTF-8";
.icon-eye:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "";
  font-size: 12px;
}

.icon-start:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "";
  font-size: 16px;
}

.icon-stop:before {
  display: inline-block;
  font-family: "Icon Font";
  content: "";
  font-size: 10px;
}
复制代码
  • @for递增数字, 语法是@for <variable> from <expression> to <expression> { ... } @for <variable> from <expression> through <expression> { ... },前者不包括最后一个数字,后者包含.
//scss
$base-color: #036;

@for $i from 1 through 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;
}

ul:nth-child(3n + 3) {
  background-color: #0059b3;
}
复制代码
  • @while循环,@while <expression> { ... }
//scss
/// Divides `$value` by `$ratio` until it's below `$base`.
@function scale-below($value, $base, $ratio: 1.618) {
  @while $value > $base {
    $value: $value / $ratio;
  }
  @return $value;
}

$normal-font-size: 16px;
sup {
  font-size: scale-below(20px, 16px);
}
复制代码
//css
sup {
  font-size: 12.36094px;
}
复制代码

@function

声明自定义函数, @function <name>(<arguments...>) { ... },用@return返回

//scss
@function pow($base, $exponent) {
  $result: 1;
  @for $_ from 1 through $exponent {
    $result: $result * $base;
  }
  @return $result;
}

.sidebar {
  float: left;
  margin-left: pow(4, 3) * 1px;
}
复制代码
//css
.sidebar {
  float: left;
  margin-left: 64px;
}

复制代码

@extend

用来扩展另一个选择器的样式@extend <selector>,只能扩展简单的选择器(比如.info),可以用逗号分隔扩展多个选择器的交集。
mixin和extend都可以复用样式,但是前者可以加参数,后者适合具有上下级关系的选择器。

@error和@warn、@debug

用在mixin和function中时用于提醒。

@at-root

@at-root <selector> { ... },对应选择器就像写在嵌套外一样。

//scss
div {
  @at-root p {
    color: aqua;
  }
}
复制代码
//css
p {
  color: aqua;
}
复制代码

也可以丢弃或选择嵌套中某些规则@at-root (with: <rules...>) { ... }@at-root (without: <rules...>) { ... }

来自css的at-rules

css中的at规则可以写作@<name> <value>,@<name> { ... }@<name> <value> { ... },比如

@namespace svg url(http://www.w3.org/2000/svg);

@font-face {
  font-family: "Open Sans";
  src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}

@counter-style thumbs {
  system: cyclic;
  symbols: "\1F44D";
}
复制代码

如果一个at规则被嵌套,输出时会提到最外层

内置模块

sass提供了好多内置模块(以sass:开头),包含很多函数,可以使用@use引入。
介绍各个模块的函数之前,先介绍全局函数

hsl($hue $saturation $lightness)
hsl($hue $saturation $lightness / $alpha)
hsl($hue, $saturation, $lightness, $alpha: 1)
hsla($hue $saturation $lightness)
hsla($hue $saturation $lightness / $alpha)
hsla($hue, $saturation, $lightness, $alpha: 1) //=> color 
复制代码

模块包括以下,具体使用参考Built-In Modules

  • sass:math 操作数字
  • sass:string 操作字符串
  • sass:color 操作颜色
  • sass:list 操作list
  • sass:map 操作map
  • sass:selector 操作选择器
  • sass:meta sass内部使用的函数

作者:卖油条的。
链接:https://juejin.cn/post/6903792503246290957
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

[声明]本站素材来自用户分享,仅限学习交流请勿用于商业用途,如损害你的权益请联系客服QQ:1098510317给予处理。
站库云-前端开发学习平台 » 2020年的css周边之sass用法指南

发表评论

提供优质的资源,供前端开发者们学习

网站案例 优化案例