Sass 代码复用有3种方式
- @mixin & @include
- @extend
- @function
@mixin & @include
用于复用样式逻辑,生成套路相同的样式。
使用 @mixin 定义样式模板。使用 @include 调用。
// index.scss
@mixin reset-list {
margin: 0;
padding: 0;
list-style: none;
}
@mixin horizontal-list {
@include reset-list;
li {
display: inline-block;
margin: {
left: -2px;
right: 2em;
}
}
}
nav ul {
@include horizontal-list;
}
/* index.css */
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav ul li {
display: inline-block;
margin-left: -2px;
margin-right: 2em;
}
参数
支持传递参数。形参要与实参长度相同。
// index.scss
@mixin rtl($property, $ltr-value, $rtl-value) {
#{$property}: $ltr-value;
[dir=rtl] & {
#{$property}: $rtl-value;
}
}
.sidebar {
@include rtl(float, left, right);
}
/* index.css */
.sidebar {
float: left;
}
[dir=rtl] .sidebar {
float: right;
}
可选参数
定义时使用 : 给参数添加默认值,使其可选。
默认值可以使用任何 SassScript 表达式,甚至可以使用之前的参数。
// index.scss
@mixin replace-text($image, $x: 50%, $y: 50%) {
text-indent: -99999em;
overflow: hidden;
text-align: left;
background: {
image: $image;
repeat: no-repeat;
position: $x $y;
}
}
.mail-icon {
@include replace-text(url("/images/mail.svg"), 0);
}
/* index.css */
.mail-icon {
text-indent: -99999em;
overflow: hidden;
text-align: left;
background-image: url("/images/mail.svg");
background-repeat: no-repeat;
background-position: 0 50%;
}
指定参数名
传递参数时,除了可以根据位置,还可以通过指定参数名。
// index.scss
@mixin square($size, $radius: 0) {
width: $size;
height: $size;
@if $radius != 0 {
border-radius: $radius;
}
}
.avatar {
@include square(100px, $radius: 4px);
}
/* index.css */
.avatar {
width: 100px;
height: 100px;
border-radius: 4px;
}
如果重构时需要修改参数名,为了兼容通过参数名传递的调用,可以将老参数名放到最后,并通过 @warn 警告用户迁移。
任意数量参数
为了接受任意数量的参数,在最后一个形参后需添加 ...。
传递实参时也可以在 list 类型数据后添加 ...。
如果实参是根据位置传递的,则形参数据类型为 list。
如果实参是通过指定参数名传递的,
通过 meta.keywords 将参数解析为 map。key 为参数名,value 为值。
如果内部没有调用 meta.keywords,不允许使用其它指定参数名参数。
代码块
类似 Vue 的插槽。
在 @mixin 内部通过 @content 使用代码块。
在 @include 后添加 {} 注入代码块。
// index.scss
@mixin color($color) {
.a {
color: $color;
@content;
}
}
@include color($color: red) {
&:hover {
color: green;
}
};
/* index.css */
.a {
color: red;
}
.a:hover {
color: green;
}
代码块参数
代码块也支持传入参数。参数使用同 @mixin。
在 @include <name> using (<arguments...>) {} 调用参数
// index.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;
}
}
}
@media screen {
h1 {
font-size: 40px;
}
}
@media print {
h1 {
font-size: 40px;
font-family: Calluna;
}
}
@extend
样式设计经常有这样一种情况:
一个 class 拥有另一个 class 的所有样式,还额外包含一些样式。
在 BEM 的思想中,会在老的 class 后添加修饰符来创建一个新的 class,并将两个类全部添加到 HTML 元素中。
这将创建多余的 HTML,并将部分 CSS 逻辑带入 HTML。
<div class="error error--serious">
Oh no! You've been hacked!
</div>
.error {
border: 1px #f00;
background-color: #fdd;
}
.error--serious {
border-width: 3px;
}
@extend 就是用来解决这类问题的。
继承包括3个专有名词:
$extender继承者,比如.error--serious$extendee被继承者,比如.error$selector匹配被继承者的选择器,包括嵌套,组合选择器,伪元素等等。比如.error和.error:hover
语法如下:
$selector { ... }
#{$extender} {
@extend #{$extendee}
}
@extend 将所有应用到 $extendee 的规则应用到 $extender 来解决上述问题。
// index.scss
.error {
border: 1px #f00;
background-color: #fdd;
&--serious {
@extend .error;
border-width: 3px;
}
}
/* index.css */
.error, .error--serious {
border: 1px #f00;
background-color: #fdd;
}
.error--serious {
border-width: 3px;
}
智能合并
@extend 通过将所有 $selector 的样式规则应用到 $selector 和 $extender 合并得到的选择器来实现继承。
Sass 合并 $selector 与 $extender 的过程非常智能。
继承发生在父选择器被编译成具体的选择器之后。
删除无用选择器
不会生成无法选中元素的选择器,比如 #main#footer
.content nav.sidebar {
@extend .info;
}
// 不会被继承,没有既是p又是nav的元素
p.info {
background-color: #dee9fc;
}
交叉
当 $selector 与 $extender 都包含后代选择器时,为了匹配不同嵌套顺序的 HTML 元素,父级选择器会交叉,生成2个规则。
// index.scss
.guide .info {
border: 1px solid rgba(#000, 0.8);
border-radius: 2px;
}
.content nav.sidebar {
@extend .info;
}
/* index.css .content 与 .guide 交叉 */
.guide .info, .guide .content nav.sidebar, .content .guide nav.sidebar {
border: 1px solid rgba(0, 0, 0, 0.8);
border-radius: 2px;
}
合并
当一个选择器匹配内容包含另一个时,只保留选择范围更小的选择器
// index.scss
.content nav.sidebar {
@extend .info;
}
main.content .info {
font-size: 0.8em;
}
/* index.css */
/* main.content 与 content 合并,只保留main.content */
main.content .info, main.content nav.sidebar {
font-size: 0.8em;
}
优先级
精简选择器的同时确保特异性大于等于 $extender 的特异性。
其它
智能地处理组合选择器,全局选择器和伪类。
// index.scss
.error:hover {
background-color: #fee;
}
.error--serious {
@extend .error;
border-width: 3px;
}
/* index.css 处理 hover 伪类*/
.error:hover, .error--serious:hover {
background-color: #fee;
}
.error--serious {
border-width: 3px;
}
占位选择器(placeholder selector)
以 % 开头命名的选择器,专门用于继承,不会生成 CSS 代码。支持私有。
// index.scss
.alert:hover, %strong-alert {
font-weight: bold;
}
%strong-alert:hover {
color: red;
}
/* index.css */
.alert:hover {
font-weight: bold;
}
继承范围
通过 @use 和 @forward引用文件,可以继承被引用文件和被递归引用文件中的选择器。
,非引用文件中的选择器不可继承。
A @use B1, C; B1 @use B2; B2 @use B3。
B1 可以继承 B2 和 B3 中的选择器。
尽管最终 B1 和 C 的样式会合并到 A,但是 B1 与 C 无引用关系,不能继承。
通过 @import 引用,则可以继承所有的选择器。
可选
如果找不到对应 $extendee 的 $selector,编译时会报错。
这能避免拼写错误导致的问题。
如果不想使用这个特性,在继承后添加 !optional
局限
- 只能继承简单独立的选择器,比如
.info或a。不能继承.message.info - 合并交叉时,只生成两个,不会遍历所有父类交叉的可能
- 在
CSS @rules内部不能继承外部的样式。
相关内置函数
selector.unify
计算两个选择器合并的结果。
@debug selector.unify("a", ".disabled"); // a.disabled
@debug selector.unify("a.disabled", "a.outgoing"); // a.disabled.outgoing
@debug selector.unify("a", "h1"); // null
@debug selector.unify(".warning a", "main a"); // .warning main a, main .warning a
selector.extend
计算继承后生成的选择器。
selector.extend($selector, $extendee, $extender)
@debug selector.extend("a.disabled", "a", ".link"); // a.disabled, .link.disabled
@debug selector.extend("a.disabled", "h1", "h2"); // a.disabled
@debug selector.extend(".guide .info", ".info", ".content nav.sidebar");
// .guide .info, .guide .content nav.sidebar, .content .guide nav.sidebar
@function
封装数据的复杂运算,生成值。最好只用作纯函数。
使用 @function 定义,内部使用 @return 返回值。
// index.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;
}
/* index.css */
.sidebar {
float: left;
margin-left: 64px;
}
参数
语法同 @mixin
其它函数
Sass内部提供了许多内置函数,用于处理各种类型的数据。CSS函数,不会被编译,但是参数可以被编译。
对比
比较
| @mixin | @function | @extend | |
|---|---|---|---|
| 接收参数 | ✔️ | ✔️ | ❌ |
| 生成样式 | ✔️ | ❌ | ✔️ |
| 生成值 | ❌ | ✔️ | ❌ |
| 修改参数 | ✔️ | ✔️ | ❌ |
适用场景
- 如果要生成值,使用
@function - 如果要生成的样式与其他类在语义上具有继承关系,使用
@extend - 只用来修改变量使用
@mixin。虽然@function可以修改变量,但是最好只用做纯函数。 - 如果要生成样式,且需要接受参数,使用
@mixin - 其他情况使用
@mixin