Sass进阶指南 -- 写出更优雅的样式表

我以为已经会了,之前在公司写项目基本都是用sass写样式,十分顺手。直到有段时间,我准备参考Element Plus来设计自己组件库的工程结构,看到Element Plus那些优雅的sass用法时,我开始为我的浅薄和无知感到羞愧。这便开始系统学习sass,期间多次想写点学习分享,却都因各种事情而无疾而终。时至今日,终于忙里偷闲有点时间,正好与君共勉。

一、简介

作为一款极其成熟的css预处理器,sass具有不少特色功能,帮助我们写出更为优雅、简洁的样式代码。它在css之前引入了样式变量,还支持嵌套、函数、混合、指令控制等功能,极大地拓展了样式玩法。

sass有两种语法格式:SCSSSass

其中SCSS是目前使用较多的一种格式,它仅在CSS3的语法基础上进行拓展,加入了Sass的特有功能,支持绝大多数的CSS hacks写法以及浏览器前缀写法(vendor-specific syntax),它的文件以.scss作为拓展名。

Sass是最早的语法格式,用缩进代替花括号,用换行代替分号,格式尤为简洁,书写也更加方便。这种格式也支持Sass的所有功能,不过在个别地方与SCSS采用了不同的表达方式,它的文件拓展名为.sass

任何一种格式都可以 导入(@import) 到另一种格式的文件中使用,也可以通过sass-convert命令行工具转化为另一种格式。

# Sass转为SCSS
sass-convert style.sass style.scss
​
# SCSS转为Sass
sass-convert style.scss style.sass 

关于Sass的安装使用就不在此介绍了,毕竟现在绝大多数项目都是通过webpack等构建工具来进行打包构建,期间辅以相应的loader等工具来辅助编译。应该极少项目需要在命令行使用sass命令来编译样式文件了吧。

不过 编码格式 还是值得一提的。和CSS一样,可以使用@hcarset来声明特定的编码格式:在样式文件的起始位置(第一个字符处)插入@charset "encoding-name"Sass就会按照给出的编码格式来编译文件。当然,使用的编码格式必须可以转化为Unicode字符集。SassUTF-8编码输出CSS文件,如果编译后的文件中出现非ASCII字符,则会在输出文件中添加@charset声明。

二、基础功能拓展

1. 嵌套规则 Nested Rules

Sass支持将一套CSS样式嵌入到另一套CSS样式中,内层的样式将以外层的选择器为父选择器。

#main p {
  color: #00ff00;
  width: 97%;
​
  .redbox {
    background-color: #ff0000;
    color: #000000;}
} 

这会被Sass编译为如下CSS

#main p {
  color: #00ff00;
  width: 97%; 
}
#main p .redbox {
  background-color: #ff0000;
  color: #000000; 
} 

嵌套功能很大程度上减少了代码量,我们不必再重复地去写繁琐的父选择器,且方便管理。

2. 父选择器 & (Referencing Parent Selectors: &)

在嵌套中,如果需要引用父选择器,例如给某个元素设置:hover样式时,或者当body元素有某个classname时,可以用&来引用父选择器,即其外层选择器。

a {
  font-weight: bold;
  text-decoration: none;
  // & 引用父元素&:hover { text-decoration: underline; }
  // body.firefox 可以直接引用
  body.firefox & { font-weight: normal; }
} 

将被Sass编译为如下CSS

a {
  font-weight: bold;
  text-decoration: none; 
}
a:hover {
  text-decoration: underline; 
}
body.firefox a {
    font-weight: normal; 
} 

&必须作为选择器的第一个字符,不过它后面可以跟随后缀来生成复合的选择器

#main {
  color: black;
  // #main-sidebar 字符串拼接,这就有点好玩了&-sidebar { border: 1px solid; }
} 

这会被编译为:

#main {
  color: black; 
}
#main-sidebar {
  border: 1px solid; 
} 

3. 属性嵌套 Nested Properties

有的CSS遵循相同的命名空间(namespace),如 font-family, font-size, font-weight 都以 font 作为属性的命名空间。这时候,为了书写简单、管理方便,Sass允许将属性嵌套在命名空间里:

.funky {
  // 注意命名空间这里要加冒号
  font: {
    family: fantasy;
    size: 30em;
    weight: bold;}
} 

编译为如下CSS

.funky {
  font-family: fantasy;
  font-size: 30em;
  font-weight: bold; 
} 

其中,命名空间也可以包含自己的属性值:

.funky {
  // font-size/line-height
  font: 20px/24px {
    family: fantasy;
    weight: bold;}
} 

编译为:

.funky {
  /* font-size/line-height */
  font: 20px/24px;
  font-family: fantasy;
  font-weight: bold; 
} 

4. 占位符选择器 %foo (Placeholder Selectors: %foo)

占位符选择器与id选择器和class选择器类似,只是符号为%,必须通过@extend指令调用,否则不会参与到Sass的编译中。

5. 注释 Comments

Sass支持单行注释//和多行注释/* */多行注释会被完整地输出到编译后的CSS文件中。将 ! 作为多行注释的第一个字符表示在压缩输出模式下保留这条注释并输出到 CSS 文件中,通常用于添加版权信息。插值语句(interpolation) 可以在多行注释中输出变量值。(使用#{$val}进行插值。)

// Sass
$version: "1.2.3";
/* This CSS is generated by My Snazzy Framework version #{$version}. */
​
// 编译为CSS:
/* This CSS is generated by My Snazzy Framework version 1.2.3. */ 

三、进阶功能拓展

1. SassScript

Sass提供了名为SassScript的强大功能,可以作用于任何属性,允许属性使用变量、算数运算等额外功能。

1.1 Interactive Shell

Interactive Shell可以在命令行中测试SassScript的功能。在命令行中输入sass -i,然后输入想要测试的SassScript查看输出结果。

sass -i
>> "Hello, Sassy World!"
"Hello, Sassy World!"
>> 1px + 1px + 1px
3px
>> #777 + #777
#eeeeee
>> #777 + #888
white 

1.2 变量 $ Variables: $

使用变量非常简单:

// 声明变量
$width: 5em;
​
#main {// 使用变量
  width: $width;
} 

变量具有块级作用域,嵌套规则内的变量属于局部变量,只能在嵌套规则内使用。而全局变量则可以在任何地方使用。添加!global声明可以把局部变量转换为全局变量

// Sass
#main {// !global声明,转换为全局变量
  $width: 5em !global;
  width: $width;
}
​
#sidebar {
  // 使用全局变量
  width: $width;
}
​
// 编译为CSS:
#main {
  width: 5em;
}
#sidebar {
  width: 5em;
} 

1.3 数据类型 Data Types

SassScript支持7种主要的数据类型:

  • 数字,可以带单位,1, 2, 3, 6, 10px等;
  • 字符串(有引号和无引号都支持),"foo", "bar", baz
  • 颜色,blue, #04a3f9, rgba(255,0,0,0.5)等;
  • 布尔值,true, false
  • 空值,null
  • 数组(list),用空格或逗号作为分隔符,1.5em 1em 0 2em, Helvetica, Arial, sans-serif
  • maps,相当于JS里的Object(key1: value1, key2: value2)

此外,SassScript也支持其它CSS属性值,如Unicode字符集,或!important声明,不过这些不会被特殊对待,而是一律视为无引号字符串。

1.3.1 字符串

SassScript支持 有引号字符串(quoted strings)无引号字符串(unquoted strings) 。这两种字符串在编译时不会相互转换,除了一种情况:在使用#{}(interpolation)时,有引号字符串会被编译为无引号字符串,这样便于在mixin中引用选择器名。

// Sass
@mixin firefox-message($selector) {
  body.firefox #{$selector}:before {
    content: "Hi, Firefox users!";}
}
@include firefox-message(".header");
​
// 编译为CSS
body.firefox .header:before {
  content: "Hi, Firefox users!"; 
} 
1.3.2 数组( lists )

数组指Sass如何处理CSSmargin: 10px 15px 0 0font-face: Helvetica, Arial, sans-serif这样通过空格或逗号分隔的一系列的值。事实上,独立的值也被视为只包含一个值的数组。

数组本身没有太多功能,不过Sass list functions给数组带来了很多新功能:nth函数可以直接访问数组中的某一项;join函数可以将多个数组连接在一起;append函数可以往数组中添加新值;@each函数可以遍历数组中的每一项。

数组中可以包含子数组:如 1px 2px, 5px 6px 是包含 1px 2px5px 6px 两个数组的数组。如果内外两层数组使用相同的分隔方式,则需要使用圆括号包裹内层,如(1px 2px) (5px 6px)。圆括号在编译时不会被保留,因此,无论哪种写法,1px 2px, 5px 6px或者(1px 2px) (5px 6px),最后的编译结果都是一样的,但是它们在Sass文件中含义不同,前者表示包含四个值的数组,后者表示包含两个子数组的数组,可以说,圆括号更强调 “分组” 的概念。

() 用于表示空数组,也可以表示空的map。空数组不会被直接编译成CSS,如果数组中有空数组或空值,在编译时会被清除。如 1px 2px () 3px1px 2px null 3px。基于逗号分隔的数组允许保留结尾的逗号,这样做的意义是强调数组的结构关系,尤其是需要声明只包含单个值的数组时。例如 (1,) 表示只包含 1 的数组。

1.3.3 maps

map是键值对的集合,keyvalue都可以是任意的SassScript对象。它和list都是用函数来操作的。例如,map-get函数可以用于查找键值,map-merge可用于添加新的键值对,@each指令可为每个键值对添加样式。map可以用在任意可以使用list的地方,反之则不能。当map用于list的函数时,会被看做key1 value1, key2 value2形式的数组。

1.3.4 颜色

任何CSS颜色表达式都会返回一个SassScript的颜色值。这包含了大量的命名颜色(往往和无引号字符串unquoted strings难以分辨)。

1.4 运算 Operations

所有数据类型都支持相等运算==!=,此外,每种数据类型也有自己的运算方式。

1.4.1 数字运算 Number Operations

SassScript支持加减乘除以及取整?运算(+, -, *, /, %),必要时会在各个单位之间转换值。关系运算符(>, <, >=, <=)也可以用于数字运算。

// Sass
p {
  width: 1in + 8pt;
}
​
// 编译为:
p {
  width: 1.111in; 
} 
  • 除法运算 /

除法运算有必要单独提一下。因为/符号在CSS中通常起到分隔数字的作用。在SassScript中,/不仅用于分隔数字,还可以用于除法运算。在以下三种情况下,/会被视为除法运算:

  • 当值或值的一部分是变量或者是函数的返回值;
  • 当值被圆括号包裹;
  • 当值是算术表达式的一部分。
// Sass
p {
  font: 10px/8px;             // 普通的CSS
  $width: 1000px;
  width: $width/2;            // 使用了变量,是除法运算
  width: round(1.5)/2;        // 函数返回值,是除法运算
  height: (500px/2);          // 使用圆括号,是除法运算
  margin-left: 5px + 8px/2px; // 算数运算表达式的一部分,是除法运算
}
​
// 编译为CSS
p {
  font: 10px/8px;
  width: 500px;
  height: 250px;
  margin-left: 9px; 
} 

如果需要使用变量,又不希望/被当作除法运算符,那么可以用#{}将变量包裹起来:

// Sass
p {$font-size: 12px;$line-height: 30px;font: #{$font-size}/#{$line-height};
}

// 编译为CSS
p {font: 12px/30px; 
} 
1.4.2 颜色值运算 Color Operations

颜色值的运算是分段进行计算的,即分别计算R, G, B的值。

p {
  color: #010203 + #040506;
} 

分段计算 01 + 04 = 05 02 + 05 = 07 03 + 06 = 09,然后编译为:

p {
  color: #050709; 
} 

可以使用color functions,这比颜色计算要方便一些。

颜色值也可以结合数字运算:

p {
  color: #010203 * 2;
}
​
// 计算 01 * 2 = 02 02 * 2 = 04 03 * 2 = 06,然后编译为:
p {
  color: #020406; 
} 

需要注意,如果颜色值包含alpha channel,则参与计算的颜色值必须拥有相同的alpha值才能进行运算,因为算数运算不会作用于alpha值。

p {
	// 需要保证有相同的alpha值color: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75);
} 

而颜色值中的alpha值,可以通过函数opacity或者transparentize进行调整。

// Sass
$translucent-red: rgba(255, 0, 0, 0.5);
p {
  color: opacify($translucent-red, 0.3);
  background-color: transparentize($translucent-red, 0.25);
}
​
// 被编译为:
p {
  color: rgba(255, 0, 0, 0.8);
  background-color: rgba(255, 0, 0, 0.25); 
} 
1.4.3 字符串运算 String Operations

使用+连接字符串;+左边的值主导最终编译结果是有引号还是无引号。

运算表达式与其他值连用时,用空格做连接符:

p {
  margin: 3px + 4px auto;
}
// 编译为:
p {
  margin: 7px auto; 
} 

可以使用#{}插入动态的值:

$value: null;
p:before {content: "I ate #{$value} pies!";
}

// 编译为CSS:
p:before {content: "I ate pies!"; 
} 
1.4.4 布尔运算 Boolean Operations

布尔型的运算支持and, or, not

1.4.5 数组运算

数组不支持直接运算,只能使用list functions来操作。

1.5 圆括号 (Parentheses)

有括号的先算括号。

1.6 函数 Functions

SassScript内置了许多函数,有些甚至可以使用普通的CSS语句调用:

p {
  color: hsl(0, 100%, 50%);
}
// 编译为:
p {
  color: #ff0000; 
} 
  • 关键词参数 Keyword Arguments

Sass函数允许使用关键词参数,上面的例子可以写为:

p {
  color: hsl($hue: 0, $saturation: 100%, $lightness: 50%);
} 

Sass内置的函数非常多,就不在此展开了。

1.7 插值语句 Interpolation #{}

通过#{}可以在选择器或属性名中使用变量:

$name: foo;
$attr: border;
p.#{$name} {#{$attr}-color: blue;
}

// 编译为:
p.foo {border-color: blue; 
} 

1.8 SassScript 中的 &

&表示父选择器。如下,&的值为((".foo.bar" ".baz.bang"), ".bip.qux")

.foo.bar .baz.bang, .bip.qux {$selector: &;
} 

如果没有父选择器,则&的值为null。这可以应用在mixin中来检测是否有父选择器:

@mixin does-parent-exist {
  @if & {
    // 有父选择器
  &:hover {
      color: red;
  }} @else {
    // 没有父选择器
    a {
      color: red;
  }}
} 

1.9 变量默认值 (Variable Defaults):!default

在变量结尾添加!default可以用来给一个未添加!default声明赋值的变量赋值。只有当变量尚未被赋值或被赋空值null时,!default的赋值才会有效。

$content: "First content";
// 变量已被赋值,无效
$content: "Second content?" !default;
$new_content: "First time reference" !default;
​
#main {
  content: $content;
  new-content: $new_content;
}
​
// 编译为:
#main {
  content: "First content";
  new-content: "First time reference"; 
} 

2. @-Rules 与指令(Directives)

Sass支持所有的CSS3 @-Rules,以及Sass特有的指令(directives)。

2.1 @import

Sass拓展了@import的功能,使其可以导入.scss.sass文件。被导入的文件将被合并编译到同一个CSS文件中去。此外,被导入的文件中包含的变量或者mixin都可以在导入的文件中使用。

一般来说,@import会寻找Sass文件并将其导入,但是以下几种情况例外,只会当作普通CSS语法:

  • 文件拓展名是.css
  • 文件名以http://开头;
  • 文件名是url()
  • @import包含media queries

允许一次导入多个文件,当没有写拓展名时,则会尝试查找.scss.sass的同名文件。

@import "rounded-corners", "text-shadow"; 

导入文件也可以使用插值语句#{},但是只能作用于CSSurl()导入方式。

$family: unquote("Droid+Sans");
@import url("http://fonts.googleapis.com/css?family=#{$family}");
​
// 编译为:
@import url("http://fonts.googleapis.com/css?family=Droid+Sans"); 

@imports还有两个特别的用法:分音嵌套

  • 分音 (Partials)

有时候想要导入Sass文件,又不希望将其编译为CSS,就可以在文件名前添加下划线,这样会告诉Sass不要对其进行编译。注意,导入语句中不需要添加下划线。例如,将文件命名为_colors.scss,则以下代码导入的是_colors.scss文件,且其不会被编译为CSS

@import "colors"; 

需要注意,如过存在同名的包含前置下划线和不包含前置下划线的文件,则添加下划线的文件会被忽略。

  • 嵌套 @import

多数场景下,我们都是在文件最顶层使用@imports。事实上,也可以把@imports嵌套进CSS样式或者@media中。与在顶层中使用的区别,在于这样导入的样式只能出现在嵌套的层中,可以理解为 “局部作用域”。

// example.scss
.example {
  color: red;
} 
// main.scss
#main {
  @import "example";
} 

最终会被编译为:

#main .example {
  color: red;
} 

但是要注意,被这种嵌套形式的@import来导入的文件中,不应有@mixin@charset等顶层指令。

2.2 @media

Sass中的@mediaCSS中一样,并且增加了新的功能:

  • 允许其在CSS规则中嵌套。当@media被嵌套进CSS规则中,编译的时候,会被编译到文件的最外层,并在内部包含之前嵌套时的父选择器,这点非常方便。
// Sass
.sidebar {
  width: 300px;
  @media screen and (orientation: landscape) {
    width: 500px;}
}
​
// 编译为
.sidebar {
  width: 300px; 
}
@media screen and (orientation: landscape) {
  .sidebar {
    width: 500px; } 
} 
  • @media允许嵌套queries,这在编译时会被自动添加and
@media screen {
  .sidebar {
    @media (orientation: landscape) {
      width: 500px;
  }}
}
​
// 编译为
@media screen and (orientation: landscape) {
  .sidebar {
    width: 500px; } 
} 
  • 可以使用SassScript(变量、函数等)来替代条件的名称或者值。
$media: screen;
$feature: -webkit-min-device-pixel-ratio;
$value: 1.5;
​
@media #{$media} and ($feature: $value) {
  .sidebar {
    width: 500px;}
}
​
// 编译为
@media screen and (-webkit-min-device-pixel-ratio: 1.5) {
  .sidebar {
    width: 500px; } 
} 

2.3 @extend

  • 通过 @extend 可以使一个选择 继承 另一个选择器的样式。
.error {
  border: 1px #f00;
  background-color: #fdd;
}
.error.intrusion {
  background-image: url("/image/hacked.png");
}
.seriousError {
  // 继承,此时所有使用到 .error 的样式都会被继承过来
  @extend .error;
  border-width: 3px;
}
​
// 编译为
.error, .seriousError {
  border: 1px #f00;
  background-color: #fdd; }
​
.error.intrusion, .seriousError.intrusion {
  background-image: url("/image/hacked.png"); 
}
​
.seriousError {
  border-width: 3px; 
} 
  • Placeholder Selectors 占位符选择器

有时候,需要定义一套用于继承的样式,而不单独给某个元素使用,我们希望它不会被Sass单独编译输出。这种场景更多出现在制作Sass样式库时。占位符选择器由此诞生。它的使用和id选择器或class选择器几乎一致,选择器符号为%。当单独使用占位符选择器时,会被Sass忽略,不会被编译到输出文件中。

// 不会被编译到输出文件中
#context a%extreme {
  color: blue;
  font-weight: bold;
  font-size: 2em;
} 

它需要通过@extend来使用:

.notice {
  @extend %extreme;
}
​
// 被编译为
#context a.notice {
  color: blue;
  font-weight: bold;
  font-size: 2em; 
} 
  • !optional 声明

使用!optional声明,可以让@extend不生成新的选择器。

// 不可以,因为没有 .notice
a.important {
  @extend .notice
}
​
// 可以
a.important {
  @extend .notice !optional;
} 
  • 在指令中使用 @extend

为了避免生成大量无用的代码,在指令中进行延伸(@extend),限制只能延伸给相同指令层级内的选择器。

@media print {
  .error {
    border: 1px #f00;
    background-color: #fdd;}
  .seriousError {
    // 可以
    // .error 同在一个 @meida层级中
    @extend .error;
    border-width: 3px;}
}
​
// 以下为反面栗子
.error {
  border: 1px #f00;
  background-color: #fdd;
}
​
@media print {
  .seriousError {
    // 不可以, .error 不在当前的@meida指令层级中
    @extend .error;
    border-width: 3px;}
} 

2.4 @at-root

使用@at-root指令可以把作用的选择器提升到最外层。

.parent {...
  @at-root .child { ... }
}
​
// 编译为
.parent { ... }
.child { ... } 
  • 它可以是一个包含多个选择器的块级结构。
.parent {...
  @at-root {
    .child1 { ... }
    .child2 { ... }}
  .step-child { ... }
}
​
// 编译为
.parent { ... }
.child1 { ... }
.child2 { ... }
.parent .step-child { ... } 
  • 通过with:xxx yyy ...without:xxx yyy ...来保证外层指令是否对提升到最外层的选择器有效:
// without:
@media print {
  .page {
    width: 8in;
    // without: media 让选择器不受外层的@media影响
    @at-root (without: media) {
      color: red;
  }}
}
​
// 编译为
@media print {
  .page {
    width: 8in;}
}
.page {
  color: red;
} 

3. 控制指令 Control Derectives

控制指令用于在一定条件下引用样式。

3.1 if()

内置的 if 函数可用于任意的SassScript脚本上下文。

3.2 @if

@if指令的表达式返回值不是false或者null时,条件成立。后面可以接@else if@else

$type: monster;
p {
  @if $type == ocean {
    color: blue;} @else if $type == matador {
    color: red;} @else if $type == monster {
    color: green;} @else {
    color: black;}
}
​
// 编译为
p {
  color: green; 
} 

3.3 @for

这个指令包含两种格式:@for $var from <start> through <end>,或者 @for $var from <start> to <end>。两者都包含起始点<start>,区别在于to不包含<end>,而through包含<end>。此外,$var可以是任何变量,但是<start><end>必须是整数。

@for $i from 1 to 3 {
  .item-#{$i} { width: 2em * $i; }
}
​
// 编译为
.item-1 {
  width: 2em; 
}
.item-2 {
  width: 4em; 
} 

3.4 @each

@each指令的格式:@each $var in <list>

@each $animal in puma, sea-slug, egret, salamander {.#{$animal}-icon {
    background-image: url('/images/#{$animal}.png');}
}
​
// 编译为
.puma-icon {
  background-image: url('/images/puma.png'); }
.sea-slug-icon {
  background-image: url('/images/sea-slug.png'); }
.egret-icon {
  background-image: url('/images/egret.png'); }
.salamander-icon {
  background-image: url('/images/salamander.png'); } 
  • 可以同时应用多个变量,在编写形式相似而值不同的样式时很方便:
@each $animal, $color, $cursor in (puma, black, default),
                                (sea-slug, blue, pointer),
                                (egret, white, move) {.#{$animal}-icon {
    background-image: url('/images/#{$animal}.png');
    border: 2px solid $color;
    cursor: $cursor;}
}
​
// 编译为
.puma-icon {
  background-image: url('/images/puma.png');
  border: 2px solid black;
  cursor: default; }
.sea-slug-icon {
  background-image: url('/images/sea-slug.png');
  border: 2px solid blue;
  cursor: pointer; }
.egret-icon {
  background-image: url('/images/egret.png');
  border: 2px solid white;
  cursor: move; } 
  • 由于maps会被当成数组lists处理,因此 @each也可以用于map
@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {#{$header} {
    font-size: $size;}
}
​
// 编译为
h1 {
  font-size: 2em; }
h2 {
  font-size: 1.5em; }
h3 {
  font-size: 1.2em; } 

3.5 @while

while循环,都懂。

$i: 6;
@while $i > 0 {
  .item-#{$i} { width: 2em * $i; }
  // 更改条件变量
  $i: $i - 2;
}
​
//编译为
.item-6 {
  width: 12em; }
​
.item-4 {
  width: 8em; }
​
.item-2 {
  width: 4em; } 

4. 混合指令 (Mixin Directives)

mixin用于定义可重复使用的样式。

4.1 定义混合指令 @minin (Defining a Mixin: @mixin)

@mixin后添加名称与样式,即可定义混合指令,注意需要包含选择器和属性,也可以使用&来引用父选择器。

// 定义名为 clearfix 的混合指令
@mixin clearfix {
  display: inline-block;
  // & 指代父选择器。注意,这个混合指令里虽然它没有父选择器,
  // 但是混合指令使用的地方,可以产生父选择器&:after {
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;}
  // 这里的 & 同理* html & { height: 1px }
} 

4.2 引用混合指令 @include (Including a Mixin: @include)

  • 使用@include MixinName来引用混合指令:
.page-title {
  // 引用混合指令,将 clearfix 中的规则混入到 .page-title 中
  @include clearfix;
  padding: 4px;
  margin-top: 10px;
} 
  • 也可以在最外层直接引用混入,不会定义属性,也没有父选择器可引用:
@mixin silly-links {
  a {
    color: blue;
    background-color: red;}
}
@include silly-links;
​
//编译为
a {
  color: blue;
  background-color: red; } 

我们编写的混合指令中,最好只定义后代选择器,这样才能安全地引入到文件的任何位置。

  • 混合指令中可以引用其它混合指令:
@mixin compound {
  @include highlighted-background;
  @include header-text;
}
@mixin highlighted-background { background-color: #fc0; }
@mixin header-text { font-size: 20px; } 

4.3 参数 (Arguments)

参数可以给混合指令的样式设置变量并赋值使用。在引用和定义混合指令时,参数的顺序应该一一对应,而且,参数可以赋默认值。

// $width有默认值
@mixin sexy-border($color, $width: 1in) {
  // 注意这里得 border,是CSS命名空间
  border: {
    color: $color;
    width: $width;
    style: dashed;}
}
p { @include sexy-border(blue); }
h1 { @include sexy-border(blue, 2in); }
​
// 编译为
p {
  border-color: blue;
  border-width: 1in;
  border-style: dashed; }
​
h1 {
  border-color: blue;
  border-width: 2in;
  border-style: dashed; } 

此外,参数还有两个比较重要的点:

  • 关键词参数 (Keyword Arguments):在引用Mixin传参时,通过指定参数关键词(参数名)来精确传值。
p { @include sexy-border($color: blue); }
h1 { @include sexy-border($color: blue, $width: 2in); } 

尽管书写稍显麻烦,但是阅读方便,且能精准赋值。

  • 参数变量 (Variable Arguments) :不确定混合指令需要多少个参数时,可以在参数最后方使用参数变量...来声明,Sass会把这些参数视为值列表来处理。
@mixin box-shadow($shadows...) {
  -moz-box-shadow: $shadows;
  -webkit-box-shadow: $shadows;
  box-shadow: $shadows;
}
.shadows {
  @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}
​
// 编译为
.shadowed {
  -moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
  -webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
  box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
} 

也可以在引用的时候使用参数变量:

@mixin colors($text, $background, $border) {
  color: $text;
  background-color: $background;
  border-color: $border;
}
$values: #ff0000, #00ff00, #0000ff;
.primary {
  @include colors($values...);
}
​
// 编译为
.primary {
  color: #ff0000;
  background-color: #00ff00;
  border-color: #0000ff;
} 

4.4 往混合指令中导入内容 @content (Passing Content Blocks to a Mixin)

引用Mixin的时候,尤其是在文件最外层引用的时候,可以通过@content占位,然后把接在@include后的内容导入到@content的位置,和模板、插槽等相似。

@mixin apply-to-ie6-only {* html {
    @content;}
}
@include apply-to-ie6-only {
  #logo {
    background-image: url(/logo.gif);}
}
​
// 编译为
* html #logo {
  background-image: url(/logo.gif);
} 

由于MixinSass样式库中使用频繁。为简化使用,可以用=表示@mixin,用+表示@include

// Sass (这里就是Sass格式的语法了,而非SCSS格式)
=apply-to-ie6-only* html
    @content
​
+apply-to-ie6-only
  #logo
    background-image: url(/logo.gif) 
  • 块级内容和变量作用域:通过@content导入的块级内容,它所在的作用域和定义它的位置的上下文相关联,而和混合指令内部的作用域没有关系。
$color: white;
@mixin colors($color: blue) {
  background-color: $color;
  // @content 的内容的作用域在定义它的地方
  @content;
  border-color: $color;
}
.colors {
  @include colors { // 这里的color,虽然会被插入到@content的位置// 但是它的作用域是在定义它的地方,
    // 而定义它的地方,$color是全局变量,值为whitecolor: $color; }
}
​
// 编译为
.colors {
  background-color: blue;
  color: white;
  border-color: blue;
} 

注意,不是每次定义的地方都是全局作用域,也有可能其定义的位置是嵌套在某个选择器里的,那时作用域为该选择器的局部作用域。

5. 函数指令 (Function Directives)

Sass允许自定义函数,函数可以在任何属性值或SassScript中使用。自定义函数命名可以加上前缀,避免命名冲突。

$grid-width: 40px;
$gutter-width: 10px;
​
@function grid-width($n) {
  @return $n * $grid-width + ($n - 1) * $gutter-width;
}
​
#sidebar { width: grid-width(5); }
​
// 编译为
#sidebar {
  width: 240px; 
} 

6. 输出格式

6.1 :nested

嵌套(:nested)是sass的默认输出格式,清晰地反映CSSHTML之间的关系。选择器与属性单独占一行,缩进量与sass文件中的保持一致,依靠缩进来反映嵌套层级。

// 编译后输出的CSS文件
#main {
  color: #fff;
  background-color: #000; }
  #main p {
    width: 10em; } 

6.2 :expanded

展开(:expanded)格式和手写时的格式一样,选择器不做缩进。

#main {
  color: #fff;
  background-color: #000;
}
#main p {
  width: 10em;
} 

6.3 :compact

紧凑格式(Compact)占用更少的空间,每条CSS规则只占用一行,嵌套过的选择器不留空行,没嵌套过的选择器之间留空行处理。

#main { color: #fff; background-color: #000; }
#main p { width: 10em; }
​
.huge { font-size: 10em; font-weight: bold; text-decoration: underline; } 

6.4 :compressed

压缩格式(Compressed)不留空行、空格、注释等,输出的体积最小。

#main{color:#fff;background-color:#000}#main p{width:10em}.huge{font-size:10em;font-weight:bold;text-decoration:underline} 

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

猜你喜欢

转载自blog.csdn.net/web22050702/article/details/128664031