# 预编译器SASS

# 变量
变量定义与ES6 let
类似, 必须先定义再使用, 且不能以数字开头, 可以包含字母、数字、下划线、中横线等
变量名中$border_color
和$border-color
编译结果是一样的, 后写的会覆盖先写的, 建议使用中横线
# 局部变量
.container {
$color: red;
}
.border {
// 此处就无法使用局部变量
border: $color;
}
# 全局变量
// 方式一 定义全局变量在括号外
$border-color: red;
.container {
color: $border-color;
}
// 方式二 使用!global关键词
.container {
$font-size: 16px !global;
}
.text {
// 此处就可以使用了
font-size: $font-size;
}
# 变量值类型
变量的值类型可以有如下几种:
- 数字: 1、2、10px、30%
- 字符串: 'foo'、'bar'
- 颜色: blue、#1ab394、rgba(255, 255, 0, 0.6)
- 布尔值: true、false
- 空值: null
- 数组(用空格或逗号作为分隔符): 1em, 2em, 3em 或者 1px 2px 3px
- maps(类似JS的对象字面量): (k1: v1, k2: v2)
// 布尔值
$black-mode: true;
// 空值 通常由函数返回表示缺少结果
$var: null;
// 数组
$font-family: 'Open Sans', Helvetica, Sans-serif;
// maps
$color: (red: #fa0000, yellow: #fbe200, blue: #95d7eb);
// maps取值, 当取不到就是空值
.content {
color: map-get($color, yellow);
// 编译结果是空, 整条 background-color 都将不存在
background-color: map-get($color, green);
}
# 默认值
$color: #333;
//如果$co1or之前没定义就使用如下的里默认值, 已定义就采用定义值
$color: #666 !default;
.container {
border-color: $color;
}
# 混合和继承
SASS
中, 复用CSS
可以采用混合@mixin
或者继承@extend
, 但是两者编译结果是不一样的
# 混合语法
- mixin是可以重复使用的组
CSS
声明 - mixin有助于减少重复代码,只需声明一次,就可在文件中引用
- 混合指令可以包含所有的
CSS
规则,绝大部分SASS
规则,甚至通过参数功能引入变量,输出多样化的样式。 - 使用参数时建议加上默认值
- 内部还可以多级嵌套, 也可以是一个函数
// $left 有缺省值
@mixin flex-align($aitem, $left: 0) {
-webkit-box-align: $aitem;
-webkit-align-items: $aitem;
-ms-flex-align: $aitem;
align-items: $aitem;
margin-left: $left
}
// 剩余参数扩展符
@mixin linear-gradients ($direction, $gradients...) {
background-color: nth($gradients, 1);
background-image: linear-gradients($direction, $gradients)
}
.container {
// @include flex-align($aitem: center) 指定参数传参
@include flex-align(center);
// 传递多个参数
@include linear-gradients(to right, red, orange, yellow);
}
# 继承语法
- 定义基础类
- 使用@extend来继承基础类
.alert {
padding: 15px;
font-size: 12px;
}
.important {
border: 1px solid #ddd;
}
// 可以继承多个基础类
.alert-info {
@extend .alert;
@extend .important;
color: #31708f;
}
.alert-success {
@extend .alert;
color: #3c763d;
}
# 一个案例
下面有一段CSS代码
, 多个类都有一部分重复的代码, 如何采用@mixin
和@extend
来解决复用重复的部分?
.base-warning {
margin: 1px 0;
font-size: 16px;
opacity: 0.8;
color: orange;
}
.base-danger {
margin: 1px 0;
font-size: 16px;
opacity: 0.8;
color: red;
}
.base-success {
margin: 1px 0;
font-size: 16px;
opacity: 0.8;
color: green;
}
使用SASS
中的@mixin
语法, 下面示例写法变得简单和优雅, 其实编译后的代码, 跟原来的CSS
代码一模一样, 有大量的重复。
@mixin base {
margin: 1px 0;
font-size: 16px;
opacity: 0.8;
}
.base-warning {
@include base;
color: orange;
}
.base-danger {
@include base;
color: red;
}
.base-success {
@include base;
color: green;
}
如此一来, CSS
的体积就会增加, 从网络的优化角度来看, 这并不是一个完美的解决方案。其实, 此处可以使用@extend
来解决重复
// 提取出公共部分为一个类
.base {
margin: 1px 0;
font-size: 16px;
opacity: 0.8;
}
.base-warning {
@extend .base;
color: orange;
}
.base-danger {
@extend .base;
color: red;
}
.base-success {
@extend .base;
color: green;
}
编译结果如下:
// 这里会出现一个多余的类.base
.base, .base-warning, .base-danger, .base-success {
margin: 1px 0;
font-size: 16px;
opacity: 0.8;
}
.base-warning {
color: orange;
}
.base-success {
color: green;
}
# 占位符
在上例中的混合
和继承
示例中, 其实还能再度优化一点, 就是使用占位符选择器
。上面的示例中的提取出的公共CSS类base
, 在实际代码中并没有使用到, 但是编译结果中会出现一个这样的类.base
。因此, 可以在基础的类.base
定义处使用占位符%base
, 编译结果就会去除掉多余的类.base
占位符选择器
类似于类选择器
,但是,它们不是以点(.)
开头,而是以百分号(%)
开头。
当在SCSS
文件中使用占位符选择器
时,它可以用于扩展其他选择器,但不会被编译成最终的CSS
%base {
margin: 1px 0;
font-size: 16px;
opacity: 0.8;
}
.base-warning {
@extend %base;
color: orange;
}
.base-danger {
@extend %base;
color: red;
}
.base-success {
@extend %base;
color: green;
}
# 运算符
# 等号操作符
符号 | 说明 |
---|---|
== | 等于 |
!= | 不等于 |
$theme: 1;
$light: true;
.container {
@if $theme == 1 {
background-color: red;
}
@else {
background-color: blue;
}
@if $light == true {
color: #fff;
}
@else {
color: black;
}
}
# 关系运行符
符号 | 说明 |
---|---|
< (lt) | 小于 |
> (gt) | 大于 |
<= (lte) | 小于等于 |
>= (gte) | 大于等于 |
$theme: 3;
.container{
// 也可以写成(@if $theme gte 5), 但是支持不好, 仍然建议使用符号
@if $theme >= 5 {
background-color:red;
}
@else{
background-color:blue;
}
}
# 逻辑运行符
符号 | 说明 |
---|---|
and | 逻辑与 |
or | 逻辑或 |
not | 逻辑非 |
$width: 100;
$height: 200;
$last: false;
$index: 5;
div {
@if $width > 50 and $height < 300 {
font-size: 16px;
}
@else {
font-size: 14px;
}
@if not ($index > 5) {
border-color: red;
}
@else {
border-color: blue;
}
}
# 数字操作符
符号 | 说明 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取模 |
数字操作的通用规律如下:
%
与单位
不能一起运算纯数字
与(百分号
或单位运算
)时会自动转化成相应的百分号
与单位值
- 乘法运算, 两个相同单位无法运算, 会报错
除法比较特殊, 以下情况会被视作为除法运算符:
- 如果值或值的一部分,是变量或者函数的返回值
- 如果值被圆括号包裹
- 如果值是算数表达式的一部分
如果需要使用变量,同时又要确保除法( / )
不做除法运算
而是完整地编译到CSS
文件中,只需要用#{}
插值语句将变量包裹
/* 加法运算 */
.content {
width: 50 + 20; // 70
width: 50 + 20%; // 70% 默认会添加单位
width: 50% + 20%; // 70%
width: 20 + 10px; // 30px
width: 10px + 20px; // 30px
width: 10px + 10pt; // 23.33333px 单位转换为px
width: 20% + 10px; // 单位无法转换, 会报错
}
/* 减法运算 */
.content {
width: 50 - 30; // 20
width: 50 - 30%; // 20%
width: 60% - 30%; // 30%
width: 20px - 10px; // 10px
width: 50px - 20pt; // 23.3333px
width: 50px - 20; // 30px
width: 50px - 18%; // 跟加法一样报错, 单位无法转换
}
/* 乘法运算 */
.content {
width: 10 * 20; // 200
width: 5 * 10%; // 50%
width: 50% * 10%; // 报错, 出现两个%, 相同单位无法运算
width: 10px * 20px; // 报错, 出现两个px, 相同单位无法运算
width: 50% * 10px; // 报错, 单位无法转换
width: 10pt * 10pt; // 报错
width: 10pt * 10px; // 报错
width: 10px * 2; // 20px
}
/* 除法运算 */
.content {
$w: 100px;
width: 10 / 5; // 不运算, 原样输出 10/5
width: (10 / 5); // 2
width: $w / 10; // 10px
width: round($number: 50) / 2; // 25
width: 10px / 2px + 3px; // 5 + 3px 结果为 8px
}
/* 取模运算 */
.content {
width: 10 % 5; // 0
width: 50 % 3px; // 2px
width: 50px % 3px; // 2px
width: 50% % 7; // 1%
width: 50px % 7; // 1px
width: 50% % 9%; // 5%
width: 50% % 10px; // 报错, 百分号仍旧无法与px转换
width: 50px % 10pt; // 10px
width: 50px + 10pt; // 63.333px 10pt == 13.333px
}
# 字符串运算
+ 运算
可用于连接字符串
注意
如果有引号字符串(位于+左侧)连接无引号字符串,运算结果是有引号的。相反,无引号字符串(位于+左侧)连接有引号字符串,运算结果则没有引 号。
如果两侧有一个值是函数返回的, 那么情况是不一样的, 无论引号''
在左边还是在右边, 结果都是带引号''
的
.container {
content: "foo" + bar; // "foobar";
content: foo + "bar"; // foobar;
content: "foo" + "bar"; // "foobar";
content: foo + bar; // foobar;
content: '' + selector-append('.a', '.b', '.c'); // '.a.b.c'
content: selector-append('.a', '.b', '.c') + ''; // '.a.b.c'
}
# 插值语句
插值语句, 允许你在字符串中插入变量、运算和其他表达式的结果, 一般使用场景如下:
选择器
属性名
属性值
注释
$class-name: danger;
$attr: color;
$author: "lorain";
$font-size: 12px;
$line-height: 30px;
/*
这是文件说明部分
@author: #{$author}
*/
a.#{$class-name} {
border-#{$attr}: #f00;
}
p {
// 这种写法就不会运算, 使得结果为需要的联合值 12px / 30px
font: #{$font-size} / #{$line-height} Helvetica;
}
# 内置模块
官网对内置模块 (opens new window)做了详细的介绍, 此处也不详细展开, 仅展示常用用法
# Color
SASS
包含很多操作颜色的函数。例如:lighten()
与darken()
函数可用于调亮或调暗颜色,opacify()
函数使颜色透明度减少,transparent()
函数使颜色透明度增加,mix()
函数可用来混合两种颜色。
p {
height:30px;
}
.p0 {
background-color:#5c7a29;
}
.p1 {
/*
让颜色变亮
lighten($color,Samount)
$amount的取值在0%-100%之间
*/
background-color: lighten(#5c7a29, 30%);
}
.p2{
/*
让颜色变暗 通常使用color.scale()代替该方案
*/
background-color: darken(#5c7a29, 15%);
}
.p3{
/*
降低颜色透明度 通常使用color.sca1e()代替该方案
background-color:opacify(#5c7a29, 0.5);
*/
background-color: opacify(rgba(#5c7a29,0.1), 0.5);
}
# String
SASS
有许多处理字符串的函数,比如向字符串添加引号的quote()
、获取字符串长度的string-length()
和将内容插入字符串给定位置的string-insert()
p {
&:after {
content:quote(这是里面的内容); // 给字符串添加引号
}
background-color: unquote($string: "#F00"); // 去掉字符串引号
z-index: str-length("sass学习");
}
# Math
Math数值函数
效处理数值计算,例如:percentage()
将无单元的数值转换为百分比,round()
将数字四舍五入为最接近的整数,min()
和max()
获取几个数字中的最小值或最大值,random()
返回一个随机数
// 使用前先导入
@use 'sass:math';
p {
z-index: abs($number: -15);// 15
z-index: ceil(5.8); // 6
z-index: max(5, 1, 6, 8, 3); // 8
opacity: random(); // 随机0-1
}
# List
List函数
操作List,length()
返回列表长度,nth()
返回列表中的特定项,join()
将两个列表连接在一起,append()
在列表未尾添加一个值
p {
z-index: length(12px); // 1
z-index: length(12px 5px 8px); // 3
z-index: index(a b c d, c); // 3
padding: append(10px 20px, 30px); // 10px 20px 30px
color: nth($list: red blue green, $n: 2); // blue
}
# Map
Map函数
操作Map
,map-get()
根据键值获取map中的对应值,map-merge()
来将两个map
合并成一个新的map
,map-values()
映射中的所有值。
$font-sizes: ("small": 12px, "normal": 18px, "large": 24px);
$padding:(top:10px, right:20px, bottom:10px, left:30px);
p {
font-size: map-get($font-sizes, "normal"); //18px
@if map-has-key($padding, "right") {
padding-right: map-get($padding, "right");
}
&:after {
content: map-keys($font-sizes) + " "+ map-values($padding) + "";
}
}
# Selector
Selector选择符相关函数
可对CSS选择进行一些相应的操作,例如:selector-append()可以把一个选择符附加到另一个选择符,selector-unify()将两组选择器合成一个复合选择器。
.header {
background-color: #000;
content: selector-append(".a", ".b", ".c") + '';
content: selector-unify("a", ".disabled") + '';
}
# 自检模块
自检相关函数,例如:feature-exists()
检查当前SASS版本
是否存在某个特性,variable-exists()
检查当前作用域中是否存在某个变量,mixin-exists()
检查某个mixin
是否存在, 自检函数通常用在代码的调试上
$color: #F00;
@mixin padding($left: 0, $top: 0, $right: 0, $bottom: 0) {
padding: $top $right $bottom $left;
}
.container {
// 此处只能使用变量名, 不可以加$
@if variable-exists(color) {
color: $color;
}
@else {
content: "$color不存在";
}
@if mixin-exists(padding) {
@include padding($left: 10px, $right: 10px);
}
}
# 流程控制
# @if
@if()
函数允许根据条件进行分支,并仅返回两种可能结果中的一种,可单独使用,也可配合混入、继承等其它语法。语法方式同 Javascript
的 if...else
if...else
.container {
// 第一种
@if(/*条件*/){
// ...
}
// 第二种
@if(/*条件*/){
// ...
} @else {
// ...
}
// 第三种
@if(/*条件*/){
// ...
}@else if(){
// ...
}@else{
// ...
}
}
示例如下:
%triangle {
width: 0px;
height: 10px;
display: inline-block;
}
@mixin triangle($direction:top,$size:38px,$border-color:black) {
border-width: $size;
border-#{$direction}-width: 0;
@if ($direction == top){
border-color: transparent transparent $border-color transparent;
border-style: dashed dashed solid dashed;
}
@else if($direction == right){
border-color: transparent transparent transparent $border-color;
border-style: dashed dashed dashed solid;
}
@else if($direction == bottom){
border-color: transparent $border-color transparent transparent;
border-style: solid dashed dashed solid;
}
@else{
border-color: dashed solid dashed dashed;
}
}
.p1 {
@extend %triangle;
@include triangle(right, 20px, red);
}
上面的编译结果如下:
.p1 {
width: 0px;
height: 0px;
display: inline-block;
}
.p1 {
border-width: 20px;
border-right-width: 0;
border-color:transparent transparent transparent red;
border-style: dashed dashed dashed solid;
}
# @for
@for
指令可以在限制的范围内重复输出格式,每次按要求(变量的值)对输出结果做出变动。这个指令包含两种格式:
@for $var from <start> through <end>
@for $var from <start> to <end>
区别在于through
与to
的含义:
- 当使用
through
时,条件范围包含<start>
与<end>
的值 - 而使用
to
时条件范围只包含<start>
的值不包含<end>
的值 $var
可以是任何变量,比如$i, 但<start>
和<end>
必须是整数值
// 从 1 到 3, 不包括 4
@for $i from 1 to 4 {
.p#{$i} {
width: 10px * $i;
height: 30px;
background-color: red;
}
}
// 从 1 到 3, 包括 3
@for $i from 1 through 3 {
.p#{$i} {
width: 10px * $i;
height: 30px;
background-color: red;
}
}
@for $i from 1 to 6 {
#loading span:nth-child(#{$i}) {
left: 20 * ($i - 1) + px;
// animation-delay: 0.2 * ($i - 1) + s;
animation-delay: unquote($string: '0.' + ($i - 1) * 2 + s);
}
}
# @each
@each 指令
类似 Javascript
中的 foreach
指令语法
$var in <list>
, $var
可以是任何变量名,比如$length
或者$name
, 而<list>
是连串的值,也就是值列表
$color-list: red green blue purple black;
@each $color in $color-list {
// 取下标, List是从1开始的
$index: index($color-list, $color);
.p#{$index - 1} {
background-color: $color;
}
}
# @while
@while指令
重复输出格式直到表达式返回结果为false
, 这样可以实现比@for
更复杂的循环
$column: 12;
@while $column > 0 {
.col-sm-#{$column} {
width: $column / 12 * 100%; // 方法一
// width: $column / 12 * 100 + %; // 会报错, 不能采用这一种
width: $column / 12 * 100#{'%'}; // 方法二, 百分号和数字间有一个空格, 如 33.333 %
width: unquote($string: $column / 12 * 100 +'%'); // 方法三
}
$column: $column - 1;
}
# 函数
SASS
中, 函数的作用是将比较复杂或者常用的内容进行封装,便于复用。函数的语法定义如下
@function func-name([$param1, $param2, ...]){
// ...
@return $value;
}
- 函数名
func-name
与func_name
效果相同 @return
它只允许在函数体中使用,并且每个@function
必须以@return
结束。 当遇到@return
时, 它会立即结束函数并返回其结果- 函数入参可以设置默认值,也可以按参数名传值,或者通过
...
表示任意参数
@function background-linear-gradient($direction: to left, $start-color: red, $end-color: green, $gradients...) {
@return linear-gradient($direction,$gradients);
}
div {
// 正常传参
background-image: background-linear-gradient(to right, blue, green);
// 省略默认值
background-image: background-linear-gradient(to right, blue);
// 按照Key进行传参
background-image: background-linear-gradient($start-color: red, $direction: to right);
}
$widths: 50px, 40px, 80px;
.top {
// 使用...给函数传递列表
width: min($widths...);
}
函数 @function
实现的某些功能混入@mixin
也能实现,但函数更侧重于做一些计算, 两者区别如下:
- 混入
mixin
主要是通过传递参数的方式输出多样化的样式,为了可以现实代码复用 - 函数的功能主要是通过传递参数后,经过函数内部的计算,最后
@return
输出一个值
# 三元条件函数
三元条件函数语法如下, 通过判断 $condition
,如果条件成立,则返回 $if-true
的结果,如果条件不成立,则返回 $if-false
的结果
if($condition, $if-true, $if-false);
/* 流程控制语句@if */
$theme: "light";
.container {
@if $theme == "light" {
color: #000;
} @else {
color: #fff;
}
}
/* 三元条件函数来改写 */
.container {
color: if($theme == 'light', #000, #fff)
}
# 模块化
# @import
Sass拓展了@import
的功能,允许其导入SCSS
或SASS
文件。被导入的文件将合并编译到同一个CSS文件
中,另外,被导入的文件中所包含的变量或者混合
指令@mixin
都可以在导入的文件中使用。
例如 public.scss
$font-base-color: #333;
在index.scss里面使用
@import "public";
$color: #666;
.container {
border-color:$color;
color:$font-base-color;
}
注意
跟我们普通CSS
里面@import
的区别, 在原生的CSS
中也有一个@import
, 但是用法不一样。如果SCSS
中使用原生方式, 不会把导入的模块当做SCSS
处理, 编译后的代码是原封不动的保留源代码, 也无法引入其中的一些变量
以下几种方式,都将作为普通的CSS
语句,不会导入任何SCSS
文件
- 文件拓展名是
.css
- 文件名以
http://
开头 - 文件名是
url
@import
包含media queries
@import "public.css";
@import url(public);
@import "http://a.com/a.css";
@import 'landscape' screen and (orientation: landscape);
# @use
从其他SASS
样式表加载mixin
, function
和 变量
,并将来自多个样式表的CSS
组合在一起,@use
加载的样式表被称为模块,多次引入只包含一次
@use
也可以看作是对@import
的增强, 使用@use
的每个文件都是一个模块, 都会有命名空间, 而@import
是全局的, 后来的引入会覆盖前面的同名属性
语法
@use '<ur1>'[as alias | namespace]
主要特性:
- 加载模块通过
@use 文件名
,如@use 'common'
@use
引入同一个文件多次,不会重复引入,而@import
会重复引入@use
引入的文件都是一个模块,默认以文件名作为模块名,可通过as aliasName
取别名, 如@use 'common' as c
@use
引入多个文件时,每个文件都是单独的模块,相同变量名不会覆盖,通过模块名访问,而@import
变量会被覆盖@use
方式可通过@use 'XXx' as *
来取消命名空间,建议不要这么做@use
模块内可通过$-
或$_
来定义私有成员,也就是说以-开头
或_开头
的Variables
、mixins
、functions
不会被引入@use
模块内变量可通过!default
定义默认值,引入时可通用with (...)
的方式修改,如@use 'common' with ($font-size: 16px)
- 可定义
-index.scss
或_index.scss
来合并多个scss文件
,它@use
默认加载文件
// global.scss
$_private-color: #1ab394; // 定义私有变量, 下划线和中划线效果一致, 都可以定义私有变量
$border-width: 2px !default; // 定义一个默认值
$colors: (red: #f00, yellow: yellow, green: green);
@mixin base {
width: 100px;
}
@function get-properity($key: red){
@return map-get($colors, $key)
}
// 默认命名空间为common
@use 'lib/common';
// 作为全局导入
@use 'lib/global' as *;
// 重命名模块名为g1
// 通过with修改global.scss内部定义的默认值
@use 'lib/global' as g1 with($border-width: 4px);
// 使用模块
.content {
// 使用引入的mixin
@include g1.base;
color: g1.get-properity(yellow);
font-size: g1.$font-size;
}
为了目录结构清晰, 一般会定义一个lib/index.scss
, 用来专门引入其他各种模块, 使用时就不必在业务代码中写入一大篇@use 'xxx'
, 只需要使用@use 'lib/index'
, 甚至还可以省略为use 'lib'
# @forward
转发@forward
可以加载一个模块的成员,并将这些成员当作自己的成员对外暴露出去,类似于类似于ES6
的export ... from
, 通常用于跨多个文件组织SASS
库
上一步通过@use 'lib'
来统一导入多个SASS
模块, 但也有对应的弊端, 里面的内部变量函数只能在导入处使用, 如果经过转发后, 就无法使用原模块的内容
例如:
// lib/index.scss
@use 'common';
@use 'global';
@use 'ui';
// 此处可以使用原来模块中的内容
$ifont-size: common.$font-size;
// 使用index.scss的模块 home.scss
@use 'lib' as index;
// 此处就无法使用lib中原来模块中的变量、函数、混入等等
$ifont-size: index.$font-size; // 报错
因此, 该种场景下就不得不使用@forward
来转发原来模块, 并将原来的成员暴露出去, 也就是将lib/index.scss
中@use
全部改写为@forward
# 全量转发
创建forward/common.scss
$font-size:14px !default;
* {
margin: 0;
padding: 0;
font-size: $font-size;
co1or: #333;
}
@function column-width($col, $total) {
@return percentage($col / $total);
}
@mixin bgColor($bg-color: #f2f2f2) {
background-color: $bg-color;
}
创建启动合并bootstrap.scss
// 包含其他很多模块, 统一转发
@forward 'forward/...';
@forward 'forward/global';
@forward 'forward/common';
使用
@use './bootstrap';
// 这里尽情使用原来common中的变量和各种内部模块
.content {
font-size: bootstrap.$font-size;
width: bootstrap.column-width(300, 1000);
background: bootstrap.bgColor();
}
# 部分转发
选择性转发,通过 hide
隐藏元素,show
显示元素,如:
// bootstrap.scss
// 只隐藏bgColor
@forward 'forward/global' hide bgColor;
// 暴露$font-size 和 column-width
@forward 'forward/common' show $font-size, column-width;
# 命名冲突
如果两个模块中, 有同名的变量或者函数等, 通过@forward
组合转发后, 在bootstrap.scss
中会发生命名空间冲突, 因此需要使用到别名as 前缀-*
来解决
例如: 两个模块forward/global.scss
和 forward/common.scss
, 都有同名变量$font-size
// forward/global.scss
$font-size: 20px;
@function column-width($col, $total) {
@return percentage($col / $total);
}
@mixin bgColor($bg-color: #f2f2f2) {
background-color: $bg-color;
}
// forward/common.scss
$font-size: 10px;
$color: green !default;
@function column-width($col, $total) {
@return percentage($col / $total);
}
@mixin bgColor($bg-color: #f2f2f2) {
background-color: $bg-color;
}
经过在forward/bootstrap.scss
组合转发后再使用
// forward/bootstrap.scss
@forward 'forward/global';
@forward 'forward/common';
使用@use 'forward/bootstrap'
就会报错, 因为两个同名的变量发生冲突了
所以, 需要在组合转发的时候加上as 前缀-*
, 使用通过bootstrap.$c-font-size
和 bootstrap.$g-font-size
// forward/bootstrap.scss
@forward 'forward/global' as g-*;
@forward 'forward/common' as c-*;
@use 'forward/bootstrap';
p {
font-size: bootstrap.$c-font-size;
padding: bootstrap.$g-font-size;
width: bootstrap.g-column-width(300, 1000);
background: bootstrap.c-bgColor();
}
注意
使用 @forward 'forward/global' as g-*;
语法的时候, 如果后面有hide
或者show
来部分导出, 后面的部分变量也需要加上同样的前缀
例如: @forward 'forward/global' as g-* hide $g-font-size;
# 定义默认值
@forward
语法同样支持with
来修改变量默认值
// 此处with中不需要加前缀
@forward 'forward/common' as c-* hide $c-font-size with( $color: red );
其他文件用@use导入上面的模块, 同时修改默认值, 就需要给@forward的with加上!default, 然后
// bootstrap.scss
@forward 'forward/common' as c-* hide $c-font-size with($color: red !default);
// home.scss
// 此处需要加前缀
@use 'forward/bootstrap' with($c-color: yellow);
# 混用转发和导入
如果需要在转发的模块内部, 使用被转发模块的内部变量和数据, 可以通过 @use
引入使用
// 转发该模块供外部使用, 先使用@forward
@forward 'uses/common'as c-*hide c-bgColor with($font-size:60px !default);
@use 'uses/code';
// 内部使用模块的变量数据, 使用@use引入一次, 但是顺序必须在@forward后面
@use 'uses/common';
.block {
background-color: code.$color;
@include common.bgColor(#fff);
}
# 嵌套处理
# 连体符 &
连体符 &
, 用于指代父选择器, 在一些特殊情况下, 也能跳出嵌套
// & 指代父选择器 .foo
// 可以看做 .foo .foo
.foo {
& {
font-size:12px;
}
}
// 可以看做 .foo .foo .bar
.foo {
& .bar {
font-size:12px;
}
}
// 可以看做 .foo .bar .foo
.foo {
.bar & {
font-size:12px;
}
}
编译结果为:
.foo {
font-size:12px;
}
.foo .bar {
font-size:12px;
}
.bar .foo {
font-size:12px;
}
与上个例子类似的还有BEM
中的使用, 如下
// 第一种情况等同于第二种情况
.block {
width: 1000px;
&__element{
font-size: 12px;
&--modifier {
font-size: 16px;
}
}
&--modifier {
font-size:14px;
}
}
// 第二种情况
.block {
width: 1000px;
@at-root #{&}__element {
font-size: 12px;
@at-root #{&}--modifier {
font-size: 16px;
}
}
@at-root #{&}--modifier {
font-size:14px;
}
}
编译为:
.block {
width: 1000px;
}
.block__element {
font-size: 12px;
}
.block__element--modifier {
font-size: 16px;
}
.block--modifier {
font-size: 14px;
}
连体符 &
相关的多种用法, 可以参考SASS中文网官方文章Sass中连体符(&)的运用 (opens new window)
还有一种特殊情况, 在媒体查询中, 该两种写法都是等同的
// 等同于第二种写法
.main {
float: left;
width: 45em;
@media (max-width: 480px) {
& {
float: none;
width: 100%;
}
}
}
.main {
float: left;
width: 45em;
& {
@media (max-width: 480px) {
float: none;
width: 100%;
}
}
}
两者都编译为:
.main {
float: left;
width: 45em;
}
@media (max-width: 480px) {
.main {
float: none;
width: 100%;
}
}
# @at-root
@at-root
用来跳出嵌套,在多级嵌套时比较常用,包含without
和with
。
//没有跳出
.parent-1 {
color:#f00;
.child {
width:100px;
}
}
//单个选择器跳出
.parent-2 {
color:#f00;
@at-root .child {
width:200px;
}
}
//多个选择器跳出
.parent-3 {
background:#f00;
@at-root {
.child1 {
width:300px;
}
.child2 {
width:400px;
}
}
}
编译为
.parent-1 {
color: #f00;
}
.parent-1 .child {
width: 100px;
}
.parent-2 {
color: #f00;
}
.child {
width: 200px;
}
.parent-3 {
background: #f00;
}
.child1 {
width: 300px;
}
.child2 {
width: 400px;
}
# without与with
默认@at-root
只会跳出选择器嵌套,而不能跳出@media
或@support
,如果要跳出这两种,则需使用@at-root (without: media)
或@at-root (without: support)
,@at-root
的关键词有四个:
- all 表示所有;
- rule 表示常规css选择器;
- media 表示media;
- support表示support(@support主要是用于检测浏览器是否支持css的某个属性)
@at-root
的默认值是@at-root (without:rule)
/*跳出父级元素嵌套*/
@media print {
.parent1 {
color:#f00;
@at-root .child1 {
width:200px;
}
}
}
/*跳出media嵌套,父级有效*/
@media print {
.parent2 {
color:#f00;
@at-root (without: media) {
.child2 {
width:200px;
}
}
}
}
/*跳出media和父级*/
@media print {
.parent3 {
color:#f00;
@at-root (without: all) {
.child3 {
width:200px;
}
}
}
}
@supports (display: flex){
.parent {
font-size: 14px;
// 跳出supports, 不跳出父元素
@at-root (without: supports){
.child {
font-size: 12px;
.son {
color: red;
}
}
}
}
}
@supports (display: flex){
.parent {
font-size: 14px;
// 不跳出supports, 跳出父元素, 跟@at-root效果一样
@at-root (with: supports){
.child-1 {
font-size: 12px;
.son-1 {
color: red;
}
}
}
}
}
编译成
/*跳出父级元素嵌套*/
@media print {
.parent1 {
color: #f00;
}
.child1 {
width: 200px;
}
}
/*跳出media嵌套,父级有效*/
@media print {
.parent2 {
color: #f00;
}
}
.parent2 .child2 {
width: 200px;
}
/*跳出media和父级*/
@media print {
.parent3 {
color: #f00;
}
}
.child3 {
width: 200px;
}
@supports (display: flex) {
.parent {
font-size: 14px;
}
}
.parent .child {
font-size: 12px;
}
.parent .child .son {
color: red;
}
@supports (display: flex) {
.parent {
font-size: 14px;
}
.child-1 {
font-size: 12px;
}
.child-1 .son-1 {
color: red;
}
}
# @at-root与 & 配合使用
.child{
@at-root .parent &{
color:#f00;
}
}
编译成
.parent .child {
color: #f00;
}
# @keyframe中使用
.demo {
animation: motion 3s infinite;
@at-root {
@keyframes motion {
}
}
}
编译成
.demo {
animation: motion 3s infinite;
}
@keyframes motion {}
# @at-root 高级用法
- 结合
@mixin
使用, 可以复用代码, 不用多次分开书写.btn
@mixin dark-theme {
@at-root .dark-theme & {
// @content 类似于 vue 的 slot 插槽
@content;
}
}
.btn {
background: transparent;
&:hover {
background: grey;
}
@include dark-theme {
background-image: linear-gradient(cornflowerblue, rebeccapurple);
}
}
编译结果
.btn {
background: transparent;
}
.btn:hover {
background: grey;
}
.dark-theme .btn {
background-image: linear-gradient(cornflowerblue, rebeccapurple);
}
Vue transition
和React Transition Group
会设置一系列的类型名,如.fade-enter
、.fade-exit
,在SASS
中我们可以直接拼接&-enter
进行复用
@mixin dark-theme($modifiers...) {
@if length($modifiers) > 0 {
@each $modifier in $modifiers {
@at-root .dark-theme &#{$modifier} {
@content;
}
}
} @else {
@at-root .dark-theme & {
@content;
}
}
}
.btn {
background: transparent;
&:hover {
background: grey;
}
@include dark-theme {
background: linear-gradient(cornflowerblue, rebeccapurple);
}
@include dark-theme(-enter) {
background: cornflowerblue;
}
@include dark-theme(-enter-active, -exit) {
background: rebeccapurple;
}
}
编译为:
.btn {
background: transparent;
}
.btn:hover {
background: grey;
}
.dark-theme .btn {
background: linear-gradient(cornflowerblue, rebeccapurple);
}
.dark-theme .btn-enter {
background: cornflowerblue;
}
.dark-theme .btn-enter-active {
background: rebeccapurple;
}
.dark-theme .btn-exit {
background: rebeccapurple;
}
# 调试API
# @debug
@debug
打印表达式的值,方便调试。
$font-sizes: 10px + 20px;
$style: (color: #bdc3c7);
.container {
@debug $style;
@debug $font-sizes;
}
# @error
@error
显示致命错误
$colors: (
blue: #c0392b,
black: #2980b9
);
@function style-variation($style) {
@error "Invalid color: '#{$style}'.";
@if map-has-key($colors, $style) {
@return map-get($colors, $style);
}
}
.container {
color: style-variation(white);
}
# @warn
@warn
显示警告性建议,会显示堆栈信息。
$colors: (
blue: #c0392b,
black: #2980b9
);
@function style-variation($style) {
@warn "Invalid color: '#{$style}'.";
@if map-has-key($colors, $style) {
@return map-get($colors, $style);
}
}
.container {
color: style-variation(white);
}
# 应用
# 主题切换
// 定义一个变量map
$themes: (
light: (
bgColor: #fff,
textColor: #000,
btnColor: #1ab394
),
dark: (
bgColor: #000,
textColor: #ddd,
btnColor: #133add
)
);
// 定义一个 useTheme 混入
$currentTheme: light;
@mixin useTheme {
@each $tk, $tv in $themes {
$currentTheme: $tk !global;
html[data-theme='#{$tk}'] & {
@content;
}
}
}
// 定义一个取变量值函数
@function getVars($properity){
$themeMap: map-get($themes, $currentTheme);
@return map-get($themeMap, $properity);
}
// 实际使用
.home {
@include useTheme {
background-color: getVars('bgColor');
color: getVars('textColor');
.button {
background: getVars('btnColor');
}
}
}
# 媒体查询
$adaptList: (
phone: (320px, 480px),
pad: (481px, 768px),
notebook: (769px, 1024px),
desktop: (1025px, 1200px),
tv: 1201px,
);
@mixin useMediaQuery ($device) {
$sizes: map-get($adaptList, $device);
@if type-of($sizes) == 'list' {
$min: nth($sizes, 1);
$max: nth($sizes, 2);
@media (min-width: $min) and (max-width: $max) {
@content;
}
}
@else {
@media (min-width: $sizes) {
@content;
}
}
}
// 使用方式
.header {
color: #ddd;
@include useMediaQuery('phone'){
font-size: 12px;
};
@include useMediaQuery('pad'){
font-size: 14px;
};
@include useMediaQuery('notebook'){
font-size: 16px;
};
@include useMediaQuery('tv'){
font-size: 20px;
};
}
# 星空效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SASS星空</title>
</head>
<body>
<div class="layer1"></div>
</body>
</html>
// 星星生成函数
@function getShadows($n) {
$shadows: '#{random(100)}vw #{random(100)}vh #fff';
@for $i from 2 through $n {
$shadows: '#{$shadows}, #{random(100)}vw #{random(100)}vh #fff';
}
@return unquote($shadows);
}
// 页面使用星星函数生成多个星星
.layer1 {
// 1-100随机数
// $size: unquote('#{random(100)}px');
$size: 1px;
$duration: 100s;
position: fixed;
width: $size;
height: $size;
border-radius: 50%;
left: 0;
top: 0;
box-shadow: getShadows(1000);
animation: moveUp $duration linear infinite;
&::after {
content: '';
position:fixed;
left: 0;
top: 100vh;
width: $size;
height: $size;
border-radius: inherit;
box-shadow: inherit;
}
}
@keyframes moveUp {
100% {
transform:translateY(-100vh);
}
}