今时不同往日,能叫我们切图仔的只能是我们自己!
在JavasScrip
框架满天飞的年代,前端三板斧之一的CSS
也前前后后涌出Sass
、Less
、Stylus
等多款CSS
预处理框架。
今天我们要讲的就是其中的的老大哥Sass
的升级版Scss
,Scss
给我们提供了变量 、循环 、继承 、混入、函数等一系列强大的功能以方便我们开发。
以前觉得Scss
简单,后来发现,简单是真的简单,我菜也是真的菜
Sass
从第三代开始,放弃了缩进式风格,并且完全向下兼容普通的CSS
代码,这一代的Sass
也被称为Scss
。
单文件编译可以在下面的网站进行操作,多文件编译见下下节,中文文档缺失的东西挺多的,有条件的可以翻看英文文档。
CSS
转Scss
:www.sass.hk/css2sass[4]Scss
转CSS
:www.sassmeister.com[5]Sass
有三个版本Dart Sass
、libsass
和Ruby Sass
:
Dart Sass
,用Dart
语言写的sass
实现,于2016年11月1日发布alpha
版本,版本1.23.0
之后完全支持模块化机制。libSass
也就是俗称的node-sass
,用c/c++
实现的sass
版本,使用非常广泛。node-sass
是绑定了 libsass
的nodejs
库,可以极快的将.scss
文件编译为.css
文件,这个安装过程……,懂的都懂,官方也不推荐再使用了。Ruby Sass
,是最初的Sass
实现,但是2019年3月26日被停止了,以后也不会再支持,使用者需要迁移到别的实现上。中文文档的安装教程是Ruby Sass
,个人推荐使用npm
安装Dart Sass
,这也是官方推荐的方式。
npm
默认安装的是Dart Sass
了。
npm install -g sass
code
--css
--index.scss
--dist
使用命令行操作,监听文件夹下的scss
文件并输出为css
文件,如果是webpack
项目,则需要使用sass-loader
。
sass --style=expanded -w css:dist --no-source-map
如果你使用的VSCode
的Live Sass Compiler
插件,可以参考我的配置,这个插件用的 Sass
版本是LibSass3.5.4
,对有些指令存在兼容性和不支持的情况。
"liveSassCompile.settings":{
"generateMap":false,
"formats":[
{
"format": "expanded",
"savePath": "~/css/",
}
]
}
嘿嘿,下面正文术语都同统一改为小写哈。
scss
提供了四种输出格式,在命令行中使用 --style
选项进行设置,在Live Sass Compiler
中配置format
参数。
「注:」dart sass
只支持expanded
和compressed
。
sass --watch style.scss:style.css --style compressed
nested
是 scss
默认的输出格式,选择器与属性等单独占用一行,缩进量与 scss
文件中一致,「每行的缩进量反映了其在嵌套规则内的层数」。
#main {
color: #fff;
background-color: #000; }
#main p {
width: 10em; }
.p {
font-size: 10em;
font-weight: bold;
text-decoration: underline; }
expanded
输出像是我们平常手写的样式,选择器、属性等各占用一行,属性根据选择器缩进,而选择器不做任何缩进。
#main {
color: #fff;
background-color: #000;
}
#main p {
width: 10em;
}
.p {
font-size: 10em;
font-weight: bold;
text-decoration: underline;
}
compact
会将每条 css
规则归纳为一行。嵌套过的选择器在输出时没有空行,不嵌套的选择器会输出空白行作为分隔符。
#main { color: #fff; background-color: #000; }
#main p { width: 10em; }
.p { font-size: 10em; font-weight: bold; text-decoration: underline; }
compressed
会删除所有无意义的空格、空白行、以及注释,力求将文件体积压缩到最小,同时也会做出其他调整,比如会自动替换占用空间最小的颜色表达方式。
#main{color:#fff;background-color:#000}#main p{width:10em}.p{font-size:10em;font-weight:bold;text-decoration:underline}
css
中重复写选择器是非常恼人的。尤其是html
结构嵌套非常深的时候,scss
的选择器嵌套可以避免重复输入父选择器,可以有效的提高开发效率,减少样式覆盖可能造成的异常问题。这也是我们最常用的功能。很多人用scss
就只用了这个功能,然后觉得自己会了。
这个是正常的css
结构
.container {
width: 1200px;
margin: 0 auto;
}
.container .header .img {
width: 100px;
height: 60px;
}
编译成scss
的样子,子元素向父元素内部嵌套了。
.container {
width: 1200px;
margin: 0 auto;
.header {
.img {
width: 100px;
height: 60px;
}
}
}
有些css
属性遵循相同的命名空间 (相同的开头),比如font-family
,font-size
,font-weight
都以font
作为属性的命名空间。为了便于管理这样的属性,也为了避免重复输入。(这个编辑器提示有点不太理想……,不是很好用)。
.container {
font: {
family: fantasy;
size: 30em;
weight: bold;
}
}
编译成css
.container {
font-family: fantasy;
font-size: 30em;
font-weight: bold;
}
命名空间也可以包含自己的属性值
.container {
color: red {
adjust: fantasy;
}
}
编译成css
.container {
color: red;
color-adjust: fantasy;
}
在嵌套 css
规则时,有时会需要使用嵌套外层的父选择器,例如,当给某个元素设定 hover
样式时,可以用&
代表嵌套规则外层的父选择器,scss
在编译时会把&
替换成父选择器名。
案例里面的&
表示的就是父级a
选择器
.container {
a {
color: #333;
&:hover {
text-decoration: underline;
color: #f00;
}
}
}
转化成scss
.container a {
color:#333;
}
.container a:hover {
text-decoration:underline;
color:#F00;
}
换个思路,也可以使用&
进行选择器名称拼接。
.main {
color: black;
&-sidebar { border: 1px solid; }
}
转化成css
.main {
color: black;
}
.main-sidebar {
border: 1px solid;
}
/* ... */
多行注释会编译到.css
文件中,compressed
(压缩)模式下除外, 将 !
作为多行注释的第一个字符表示在压缩输出模式下也保留这条注释,通常用于添加版权信息。
/*!
* 我是
* 多行
* 注释
*/
body { color: black; }
编译成css
/*!
* 我是
* 多行
* 注释
*/body{color:#000}
//
单行注释不会编译到.css
文件
// 我是单行注释
body { color: black; }
编译成css
body {
color: black;
}
原生css
中的变量,使用--变量名:变量值
定义,var(--变量名)
进行使用。
:root {
--color: #F00;
}
p {
color: var(--color);
}
scss
中的变量,以美元符号$
开头,赋值方法与 css
属性的写法一样。
$color:#F00;
p {
color: $color;
}
转行成css
p {
color: #F00;
}
下文的mixin
和function
命名也遵循1234
条规则:
$
开头,后面跟变量名;-
与下划线_
定义的同名变量为同一变量;css
,即变量名和值之间用冒号:
分隔;2 . 将局部变量转换为全局变量可以添加!global
声明。
$color: red;
.container {
$height: 500px;
$font-size: 16px !global;
font-size: $font-size;
color: $color;
height: $height;
}
.footer {
/**$font-size使用!global 申明成全局变量了*/
font-size: $font-size;
/**
* Error: Undefined variable.
* $height是.container下的局部变量,无法在.footer下面编译
*/
height:$height;
}
编译成css
.container {
font-size: 16px;
color: red;
height: 500px;
}
.footer {
/**$font-size使用!global 申明成全局变量了*/
font-size: 16px;
}
scss
支持7
种主要的数据类型:
1rem、2vh、13、 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
, 相当于 JavaScript
的 object
,(key1: value1, key2: value2)
;官网中把Function
也当作一种类型,点击原文了解[6]。
$layer-index: 10;
$border-width: 3px;
$font-weight: bold;
$font-base-family: "Open Sans", Helvetica, Sans-Serif;
$block-base-padding: 6px 10px 6px 10px;
$top-bg-color: rgba(255, 147, 29, 0.6);
$blank-mode: true;
$var: null;
$fonts: (
serif: "Helvetica Neue",
monospace: "Consolas",
);
.container {
font-family: $font-base-family;
font-size: $font-size;
padding: $block-base-padding;
@if $blank-mode {
background-color: #000;
} @else {
background-color: #fff;
}
content: type-of($var);
content: length($var);
color: $top-bg-color;
}
// 如果列表中包含空值,则生成的CSS中将忽略该空值。
.wrap {
font: 18px $font-weight map-get($fonts, "sans");
}
编译成css
.container {
font-family: "Open Sans", Helvetica, Sans-Serif;
font-size: 16px;
padding: 6px 10px 6px 10px;
background-color: #000;
content: null;
content: 1;
color: rgba(255, 147, 29, 0.6);
}
.wrap {
font: 18px bold; // 如果列表中包含空值,则生成的CSS中将忽略该空值。
}
scss
属性也支持其他值,比如Unicode
字符集,或!important
声明。但是scss
不会特殊对待这些属性值,一律视为无引号字符串。
$color:red;
.container {
color:$color !important;
}
编译成css
.container {
color: red !important;
}
可以在变量的结尾添加!default
来给变量设置默认值,有点类似Javascript
的逻辑运算符let content=content || "Second content"
。注意,变量是 null
时将视为未被!default
赋值。
$content: "First content";
// 如果$content之前没定义就使用如下的默认值
$content: "Second content" !default;
#main {
content: $content;
}
编译成css
#main {
content: "First content";
}
圆括号()
可以用来影响运算的顺序,和数学中的效果是一致的。
相等运算==
和不相等运算!=
。所有数据类型均支持 ==
和!=
,另外,每种数据类型也有其各自支持的运算方式。
$theme:"blue";
.container {
@if $theme=="blue" {
background-color: red;
}
@else {
background-color: blue;
}
}
.container {
@if $theme !="blue" {
background-color: red;
}
@else {
background-color: blue;
}
}
编译为css
.container {
background-color: red;
}
.container {
background-color: blue;
}
四个关系运算符< > >= <=
$theme:3;
.container {
@if $theme >= 5 {
background-color: red;
}
@else {
background-color: blue;
}
}
编译为css
.container {
background-color: blue;
}
三个布尔运算符and or not
$width: 100;
$height: 200;
$last: false;
div {
@if $width>50 and $height<300 {
font-size: 16px;
} @else {
font-size: 14px;
}
@if not $last {
border-color: red;
} @else {
border-color: blue;
}
@if $width>500 or $height<300{
line-height: 20px;
} @else {
line-height: 50px;
}
}
编译为css
div {
font-size: 16px;
border-color: red;
line-height: 20px;
}div {
font-size: 16px;
border-color: red;
}
+ - * / %
/* 纯数字与百分号或单位运算时会自动转化成相应的百分比与单位值 */
.container {
/* ================== + 运算===================== */
width: 50 + 20;
width: 50 + 20%;
width: 50% + 20%;
width: 10pt + 20px;
width: 10pt + 20;
/* ================== - 运算===================== */
height: 50 - 20;
height: 10 - 20%;
height: 50pt - 20px;
/* ================== * 运算===================== */
height: 50 * 30;
height: 10 * 30%;
height: 50 * 2px;
height: 50pt * 4;
/* ==================/ 运算 (除完后最多只能保留一种单位)===================== */
$width: 100px;
width: 10/5;
width: 10px / 5px;
width: 10px / 20;
width: ($width/2); // 使用变量与括号
height: (500px/2); // 使用了括号
/* ==================% 求余运算===================== */
width: 10 % 3;
width: 50px % 7;
width: 50% % 7;
}
编译成css
/* 纯数字与百分号或单位运算时会自动转化成相应的百分比与单位值 */
.container {
/* ================== + 运算===================== */
width: 70;
width: 70%;
width: 70%;
width: 25pt;
width: 30pt;
/* ================== - 运算===================== */
height: 30;
height: -10%;
height: 35pt;
/* ================== * 运算===================== */
height: 1500;
height: 300%;
height: 100px;
height: 200pt;
/* ==================/ 运算 (除完后最多只能保留一种单位)===================== */
width: 10/5;
width: 10px/5px;
width: 10px/20;
width: 50px;
height: 250px;
/* ==================% 运算===================== */
width: 1;
width: 1px;
width: 1%;
}
/
在css
中有分隔数字的用途,在scss
中,以下三种情况会进行除法运算:
$width: 1000px;
div {
font: 16px/30px Arial, Helvetica, sans-serif; // 不运算
width: ($width/2); // 使用变量与括号
width: (#{$width}/2); // 使用 #{} 插值语句将变量包裹,避免运算。
z-index: round(10)/2; // 使用了函数
height: (500px/2); // 使用了括号
margin-left: 5px + 8px/2px; // 使用了+表达式
}
编译成css
div {
font: 16px/30px Arial, Helvetica, sans-serif;
width: 500px;
width: 1000px/2;
z-index: 5;
height: 250px;
margin-left: 9px;
}
如果需要使用变量,同时又要确保 /
不做除法运算而是完整地编译到 css
文件中,只需要用 #{}
插值语句将变量包裹。
+
可用于连接字符串;.container {
content: "Foo " + Bar;
font-family: sans- + "serif";
}
编译为css
.container {
content: "Foo Bar";
font-family: sans-serif;
}
文章上面有讲到插值语句,这里来解释一下。
通过 #{}
插值语句可以在选择器、属性名、注释中使用变量,使用#{}
插值语句将变量包裹起来即可,和js
中的模板字符串很像。
$font-size: 12px;
$line-height: 30px;
$class-name: danger;
$attr: color;
$author: "福大命大";
p {
font: #{$font-size}/#{$line-height} Arial Helvetica, sans-serif;
}
/*
* 这是文件的说明部分
* @author: #{$author}
*/
a.#{$class-name} {
border-#{$attr}: #f00;
}
编译成css
p {
font: 12px/30px Arial Helvetica, sans-serif;
}
/*
* 这是文件的说明部分
* @author: 福大命大
*/
a.danger {
border-color: #f00;
}
sass
中流程控制包含四类,也是我们在js
中常见的@if、@for、@each、@while
。
@if
语法和js
类似,基本格式是@if...@else if...@else
。
$theme:3;
.container {
@if $theme >= 5 {
background-color: red;
}
@else {
background-color: blue;
}
}
编译为css
.container {
background-color: blue;
}
这里已一个利用mixin
和if
封装一个三角形生成,mixin
知识后面又讲到。
@mixin triangle($direction:top, $size:30px, $border-color:black) {
width: 0px;
height: 0px;
display: inline-block;
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: $border-color transparent transparent transparent;
border-style: solid dashed dashed dashed;
}
@else if($direction==left) {
border-color: transparent $border-color transparent transparent;
border-style: dashed solid dashed dashed;
}
}
.p0 {
@include triangle($size:50px);
}
.p1 {
@include triangle(right, 50px, red);
}
.p2 {
@include triangle(bottom, 50px, blue);
}
.p3 {
@include triangle(left, 50px, green);
}
编译为
.p0 {
width: 0px;
height: 0px;
display: inline-block;
border-width: 50px;
border-top-width: 0;
border-color: transparent transparent black transparent;
border-style: dashed dashed solid dashed;
}
.p1 {
width: 0px;
height: 0px;
display: inline-block;
border-width: 50px;
border-right-width: 0;
border-color: transparent transparent transparent red;
border-style: dashed dashed dashed solid;
}
.p2 {
width: 0px;
height: 0px;
display: inline-block;
border-width: 50px;
border-bottom-width: 0;
border-color: blue transparent transparent transparent;
border-style: solid dashed dashed dashed;
}
.p3 {
width: 0px;
height: 0px;
display: inline-block;
border-width: 50px;
border-left-width: 0;
border-color: transparent green transparent transparent;
border-style: dashed solid dashed dashed;
}
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>
必须是整数值。@for $i from 1 to 3 {
#loading span:nth-child(#{$i}) {
width: 20 * ($i - 1) + px;
}
}
编译为
#loading span:nth-child(1) {
width: 0px;
}
#loading span:nth-child(2) {
width: 20px;
}
如果把to
换成through
#loading span:nth-child(1) {
width: 0px;
}
#loading span:nth-child(2) {
width: 20px;
}
#loading span:nth-child(3) {
width: 40px;
}
@each
指令的格式是@each $var in $list
, $var
可以是任何变量名,比如$length
或者$name
,而$list
是一连串的值,也就是值列表。
$color-list:red green blue turquoise darkmagenta;
@each $color in $color-list {
$index: index($color-list, $color);
.p#{$index - 1} {
background-color: $color;
}
}
编译为
.p0 {
background-color: red;
}
.p1 {
background-color: green;
}
.p2 {
background-color: blue;
}
.p3 {
background-color: turquoise;
}
.p4 {
background-color: darkmagenta;
}
@while
指令循环输出直到表达式返回结果为 false
。这样可以实现比@for
更复杂的循环。
比如,可以借此生成栅格化布局。
$column:12;
@while $column>0 {
.col-sm-#{$column} {
width: $column / 12 * 100%;
}
$column:$column - 1;
}
编译为
.col-sm-12 {
width: 100%;
}
.col-sm-11 {
width: 91.6666666667%;
}
.col-sm-10 {
width: 83.3333333333%;
}
.col-sm-9 {
width: 75%;
}
.col-sm-8 {
width: 66.6666666667%;
}
.col-sm-7 {
width: 58.3333333333%;
}
.col-sm-6 {
width: 50%;
}
.col-sm-5 {
width: 41.6666666667%;
}
.col-sm-4 {
width: 33.3333333333%;
}
.col-sm-3 {
width: 25%;
}
.col-sm-2 {
width: 16.6666666667%;
}
.col-sm-1 {
width: 8.3333333333%;
}
@import
算是一个比较简易的模块系统。scss
拓展了@import
的功能,允许其导入 scss
或 sass
文件。被导入的文件将合并编译到同一个 css
文件中,被导入的文件中所包含的变量或者混合指令 (mixin
) 都可以在导入的文件中使用。
common.scss
$color:red;
index.scss
@import "common.scss";
.container {
border-color: $color;
}
编译成
.container {
border-color: red;
}
以下情况下,@import
仅作为普通的css
语句,不会导入scss
文件:
.css
;http://
开头;url()
;@import
包含媒体查询。@import "common.css";
@import url(common);
@import "http://xxx.com/xxx";
@import 'landscape' screen and (orientation:landscape);
scss
允许同时导入多个文件,例如同时导入 rounded-corners
与 text-shadow
两个文件,不用再单独写个import
引入。
@import "rounded-corners", "text-shadow";
导入文件也可以使用 #{}
插值语句(下面有讲,这里把它理解成js
中模板字符串就行)动态导入,但不是通过变量动态导入 scss
文件,只能作用于 css
的 url()
导入方式
$family: unquote("Droid+Sans");
@import url("http://fonts.googleapis.com/css?family=#{$family}");
编译为
@import url("http://fonts.googleapis.com/css?family=Droid+Sans");
如果需要导入 scss
或者 sass
文件,但又不希望将其编译为 css
,只需要在文件名前添加下划线,这样会告诉 scss
不要编译这些文件。注意:
「_common.scss」
$color:red;
「index.scss」
@import "common.scss";
.container {
border-color: $color;
}
编译为
.container {
border-color: red;
}
_common.scss
文件不会编译成_common.css
文件。
Partials
主要是用来定义公共样式的,专门用于被其他的 scss
文件 import
进行使用的。
大多数情况下,一般在文件的最外层(不在嵌套规则内)使用@import
,其实,也可以将@import
嵌套进内层选择器或者 @media
中,与平时的用法效果相同,只是这样导入的样式只能出现在嵌套的层中,存在作用域。
common.scss
.example {
color: red;
}
index.scss
#main {
@import "example";
}
被编译成
#main .example {
color: red;
}
注意:@import
不能嵌套使用在控制指令或混入中(带有@
符号的叫指令)。
scss
中,@media
指令与 css
中用法一样,只是增加了一点额外的功能,允许在css
规则中嵌套。如果@media
嵌套在 css
规则内,编译时,@media
将被编译到文件的最外层,包含嵌套的父选择器。这个让 @media
方便不少,不需要重复写选择器,也不会打乱 css
的书写流程。
.sidebar {
width: 300px;
@media screen and (orientation: landscape) {
width: 500px;
.item {
height: auto;
}
}
}
编译为
.sidebar {
width: 300px;
}
@media screen and (orientation: landscape) {
.sidebar {
width: 500px;
}
.sidebar .item {
height: auto;
}
}
@media
允许互相嵌套使用,编译时,scss
自动添加 and
@media screen {
.sidebar {
@media (orientation: landscape) {
width: 500px;
}
}
}
编译为
@media screen and (orientation: landscape) {
.sidebar {
width: 500px;
}
}
可以使用变量,函数,以及运算符代替条件的名称或者值。
$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;
}
}
混合指令(Mixin
)用于定义可重复使用的样式。混合指令可以包含所有的css
规则,绝大部分 scss
规则,甚至可以通过参数功能引入变量,输出多样化的样式。
「注意:函数命名和变量命名规则一致。」
@mixin mixin-name() {
/* css 声明 */
}
// 使用
@include mixin-name;
// 定义一个区块基本的样式
@mixin block {
width: 96%;
margin-left: 2%;
border-radius: 8px;
border: 1px #f6f6f6 solid;
}
// 使用混入
.container {
.block {
@include block;
}
}
编译为
.container .block {
width: 96%;
margin-left: 2%;
border-radius: 8px;
border: 1px #f6f6f6 solid;
}
@mixin
里面再嵌套一层
@mixin warning-text {
font-size: 12px;
.warn-text {
color: rgb(255, 253, 123);
line-height: 180%;
}
}
.container {
@include warning-text;
}
编译为
.container {
font-size: 12px;
}
.container .warn-text {
color: #fffd7b;
line-height: 180%;
}
// 定义flex布局元素纵轴的排列方式
@mixin flex-align($aitem) {
align-items: $aitem;
}
// 只有一个参数,直接传递参数
.container {
@include flex-align(center);
}
编译为
.container {
align-items: center;
}
// 定义块元素内边距
@mixin block-padding($top, $right, $bottom, $left) {
padding-top: $top;
padding-right: $right;
padding-bottom: $bottom;
padding-left: $left;
}
// 按照参数顺序赋值
.container1 {
@include block-padding(10px, 20px, 30px, 40px);
}
// 可指定参数赋值
.container2 {
@include block-padding($left: 20px, $top: 10px, $bottom: 10px, $right: 30px);
}
// 可指定参数赋值,但是必须指定4个值,不能缺失
.container3 {
@include block-padding($left: 10px, $top: 10px, $bottom: 0, $right: 0);
}
编译为
.container1 {
padding-top: 10px;
padding-right: 20px;
padding-bottom: 30px;
padding-left: 40px;
}
.container2 {
padding-top: 10px;
padding-right: 30px;
padding-bottom: 10px;
padding-left: 20px;
}
.container3 {
padding-top: 10px;
padding-right: 0;
padding-bottom: 0;
padding-left: 10px;
}
// 定义块元素内边距,参数指定默认值
@mixin block-padding($top:0, $right:0, $bottom:0, $left:0) {
padding-top: $top;
padding-right: $right;
padding-bottom: $bottom;
padding-left: $left;
}
// 可指定参数赋值
.container {
/** 不带参数 */
@include block-padding;
/** 按顺序指定参数值 */
@include block-padding(10px,20px);
/** 给指定参数指定值 */
@include block-padding($left: 10px, $top: 20px)
}
编译为
.container {
/** 不带参数 */
padding-top: 0;
padding-right: 0;
padding-bottom: 0;
padding-left: 0;
/** 按顺序指定参数值 */
padding-top: 10px;
padding-right: 20px;
padding-bottom: 0;
padding-left: 0;
/** 给指定参数指定值 */
padding-top: 20px;
padding-right: 0;
padding-bottom: 0;
padding-left: 10px;
}
使用...
处理参数不固定的情况,类似于js
中的函数的剩余参数
@mixin linear-gradient($direction, $gradients...) {
background-color: nth($gradients, 1);
background-image: linear-gradient($direction, $gradients);
}
.table-data {
@include linear-gradient(to right, #F00, orange, yellow);
}
编译为
.table-data {
background-color: #F00;
background-image: linear-gradient(to right, #F00, orange, yellow);
}
mixin
是可以重复使用的一组css
声明,有助于减少重复代码,只需声明一次,就可在文件中引用;css
规则,绝大部分scss
规则,可以传递参数,输出多样化的样式;@import
导入局部模块化样式(类似功能、同一组件);@minix
定义的是可重复使用的样式@function
用于封装复杂的操作,可以很容易地以一种可读的方式抽象出通用公式和行为,函数提供返回值,常用来做计算方面的工作。
注意:函数命名和变量命名规则一致。
@function square($base) {
@return $base * $base * 1px;
}
.sidebar {
float: left;
margin-left: square(4);
}
编译为
.sidebar {
float: left;
margin-left: 16px;
}
默认值可以是任何表达式,它们甚至可以引用前面的参数!
//change-color和hue是内置方法
//hue 返回$color的颜色为0到360度之间的一个数字。
//change-color 用于设置颜色的属性
@function invert($color, $amount: 100%) {
//@error hue($color); 调试 210deg
$inverse: change-color($color, $hue: hue($color) + 180);
@return mix($inverse, $color, $amount);
}
$primary-color: #036;
.header {
background-color: invert($primary-color, 80%);
}
编译为
.header {
background-color: #523314;
}
$primary-color: #036;
.banner {
//scale-color Fluidly scales one or more properties of .$color
background-color: $primary-color;
color: scale-color($primary-color, $lightness: +40%);
}
编译为
.banner {
background-color: #036;
color: #0a85ff;
}
参数列表还可用于采用任意关键字参数,meta.keywords()
函数采用参数列表
@function sum($numbers...) {
$sum: 0;
@each $number in $numbers {
$sum: $sum + $number;
}
@return $sum;
}
$widths: 50px, 30px, 100px;
.micro {
width: sum($widths...);
}
编译为
.micro {
width: 180px;
}
@return
只允许在@function
内使用,和js
一样,遇到return
就会返回。
@function red() {
$is: true;
@if $is {
@return 'is';
}
@return red;
}
.con{
color: red();
}
编译为
.con {
color: "is";
}
总结:
@function
和@mixin
参数的使用方式没啥区别;
@function
用来计算,@mixin
用来封装样式,@import
用来抽离他们为一个模块。
我们以elementUI
的el-button
组件为例,可以使用@extend
继承已经存在的样式,原理是使用逗号选择器。
// # id选择器一样的
.button {
display: inline-block;
margin-bottom: 0;
font-weight: normal;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
border-radius: 4px;
user-select: none;
}
.btn-default {
@extend .button;
color: #333;
background-color: #fff;
border-color: #ccc;
}
.btn-danger {
@extend .button;
color: #fff;
background-color: #d9534f;
border-color: #d43f3a;
}
编译成
.button, .btn-danger, .btn-default {
display: inline-block;
margin-bottom: 0;
font-weight: normal;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
border-radius: 4px;
user-select: none;
}
.btn-default {
color: #333;
background-color: #fff;
border-color: #ccc;
}
.btn-danger {
color: #fff;
background-color: #d9534f;
border-color: #d43f3a;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
font-size: 12px;
}
.important {
font-weight: bold;
font-size: 14px;
}
.alert-danger {
@extend .alert;
@extend .important;
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
编译为
.alert, .alert-danger {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
font-size: 12px;
}
.important, .alert-danger {
font-weight: bold;
font-size: 14px;
}
.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
@extend
可以多层继承,列如:.alert-danger
继承自.important
,.important
又继承自.alert
。
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
font-size: 12px;
}
.important {
@extend .alert;
font-weight: bold;
font-size: 14px;
}
.alert-danger {
@extend .important;
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
编译为
.alert, .important, .alert-danger {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
font-size: 12px;
}
.important, .alert-danger {
font-weight: bold;
font-size: 14px;
}
.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
占位符选择器%
,与常用的id
与 class
选择器写法相似,只是 #
或 .
替换成了%
,占位符选择器必须通过 @extend
指令调用。
还是上面的例子,这里使用占位符选择器操作
.button %base {
display: inline-block;
margin-bottom: 0;
font-weight: normal;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
border-radius: 4px;
user-select: none;
}
.btn-default {
@extend %base;
color: #333;
background-color: #fff;
border-color: #ccc;
}
.btn-danger {
@extend %base;
color: #fff;
background-color: #d9534f;
border-color: #d43f3a;
}
效果和上面的类选择器一样,但是,他有个有优点,占位符选择器%
所属的样式未使用时,不会被编译到css
文件中,算是一个小优化吧。
.button .btn-danger, .button .btn-default {
display: inline-block;
margin-bottom: 0;
font-weight: normal;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
border-radius: 4px;
user-select: none;
}
.btn-default {
color: #333;
background-color: #fff;
border-color: #ccc;
}
.btn-danger {
color: #fff;
background-color: #d9534f;
border-color: #d43f3a;
}
存在兼容性问题,仅在Dart Sass 1.23.0
以上有效,官方文档有兼容性介绍[7]。
scss
真正意义上的模块化,可以从其它 scss
样式表中加载mixin
、function
和变量
,并将来自多个样式表的 css
组合在一起。scss
还提供了很多内置模块,我们可以通过@use
使用。
classname
,因此起名总是需要注意。import
的地方,这对ui
库不够友好。src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
index.scss
@use "src/corners";
.button {
@include corners.rounded;
padding: 5px + corners.$radius;
}
src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
index.scss
@use "src/corners" as c;
.button {
@include c.rounded;
padding: 5px + c.$radius;
}
编译为
.button {
border-radius: 3px;
padding: 8px;
}
使用as*
,那么这一模块就处于全局命名空间。
src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
index.scss
@use "src/corners" as *;
.button {
@include rounded;
padding: 5px + $radius;
}
编译为
.button {
border-radius: 3px;
padding: 8px;
}
使用scss
可以轻松地定义私有成员,私有成员命名以-
或开头。
src/_corners.scss
$-radius: 3px;
@mixin rounded {
border-radius: $-radius;
}
index.scss
@use "src/corners";
.button {
@include corners.rounded;
// Error: Private members can't be accessed from outside their modules
padding: 5px + corners.$-radius;
}
@forward
语句可以引入另一个模块的所有变量、mixins
和函数,将它们直接作为当前模块的API
暴露出去,不会真的在当前模块增加代码。不同于 @use
, @forward
不能给变量添加命名空间。
注意,此时生成的bootstrap.css
文件中,是不包含functions
、variables
、mixins
代码的,也不能直接在bootstrap.scss
文件中使用这些模块。而是需要在另一个文件中 @import 'bootstrap'
或者 @use bootstrap
模块,再去使用这些方法。bootstrap.scss
文件类似于一个传输中转站,把上下游的成员变量无缝连接起来。
/* bootstrap.scss */
@forward"functions";
@forward"variables";
@forward"mixins";
注意,直接写在上游模块的正常的样式仍然会被 @forward
进来。见下例:
a.scss
@mixin rounded {
border-radius: 100px;
}
footer {
height: 1px;
}
b.scss
$radius: 3px;
c.scss
@forward "a";
@forward "b";
index.scss
@import "c.scss";
.button {
@include rounded;
padding: $radius;
}
编译为
footer {
height: 1px;
}
.button {
border-radius: 100px;
padding: 3px;
}
通过控制 show
和 hide
,可以决定模块中的哪些成员对引入后的模板可见。对隐藏的变量,在下游文件中不可以使用,相当于模块私有成员。
c.scss
@forward "a" show rounded;
@forward "b" hide $radius;
index.css
@import "c.scss";
.button {
@include rounded;
padding: $radius;
}
// Error: Undefined variable. padding: $radius;
大多数情况下,一个样式库会存在一个入口文件index.scss
,然后在index.scss
中引入其他的子文件。这种结构类似于一个多合一模块。那么,如果要在某一文件中 @forward
多个子模块,就可以使用as <prefix>-*
语句,为子模块下的成员自动带上前缀以区分。
c.scss
@forward "a" as mixin-*;
@forward "b" as var-*;
index.css
@import "c.scss";
.button {
@include mixin-rounded;
padding: $var-radius;
}
很多内置的方法就是这样使用的,嘿嘿!
@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;
}
默认@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;
}
}
}
}
编译成
/*跳出父级元素嵌套*/
@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;
}
.child{
@at-root .parent &{
color:#f00;
}
}
编译成
.parent .child {
color: #f00;
}
.demo {
animation: motion 3s infinite;
@at-root {
@keyframes motion {
}
}
}
编译成
.demo {
animation: motion 3s infinite;
}
@keyframes motion {}
scss
内置扩展分为color list map math meta selector string
等,扩展也就是scss
内置的一些function
,每个模块下内容比较多,这里用一些常用的进行举例。
内置函数可以使用@use
模块化引入,也可以直接使用他提供的全局函数名调用,以下两种方式是一样的。
@use 'sass:list';
p {
color: nth($list: red blue green, $n: 2); // blue
color: list.nth($list: red blue green, $n: 2); // blue
}
scss
包含很多操作颜色的函数。例如lighten()
与 darken()
可用于调亮或调暗颜色,opacify()
使颜色透明度减少,transparent()
使颜色透明度增加,mix()
用来混合两种颜色。
.p1 {
// 让颜色变亮
color:scale-color(#5c7a29, $lightness: +30%);
}
.p2 {
// 让颜色变暗
color:scale-color(#5c7a29, $lightness: -15%);
}
.p3 {
// 降低颜色透明度
color:scale-color(#5c7a29, $alpha: -40%);
}
编译为
.p1 {
color: #95c249;
}
.p2 {
color: #4e6823;
}
.p3 {
color: rgba(92, 122, 41, 0.6);
}
scss
有许多处理字符串的函数,比如向字符串添加引号的quote()
、获取字符串长度的string-length()
和将内容插入字符串给定位置的string-insert()
。
p {
&:after {
content: quote(这是里面的内容);
}
background-color: unquote($string: "#F00");
z-index:str-length("scss学习");
}
编译为
p {
background-color: #F00;
z-index: 6;
}
p:after {
content: "这是里面的内容";
}
数值函数处理数值计算,例如:percentage()
将无单元的数值转换为百分比,round()
将数字四舍五入为最接近的整数,min()
和max()
获取几个数字中的最小值或最大值,random()
返回一个随机数。
p {
z-index: abs(-15); // 15
z-index: ceil(5.8); //6
z-index: max(5, 1, 6, 8, 3); //8
opacity: random(); // 随机 0-1
}
编译为
p {
z-index: 15;
z-index: 6;
z-index: max(5, 1, 6, 8, 3);
opacity: 0.8636254167;
}
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
}
编译为
p {
z-index: 1;
z-index: 3;
z-index: 3;
padding: 10px 20px 30px;
color: blue;
}
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) + "";
}
}
编译为
p {
font-size: 18px;
padding-right: 20px;
}
p:after {
content: '"small", "normal", "large" 10px, 20px, 10px, 30px';
}
选择符相关函数可对选择css
进行一些相应的操作,例如:selector-append()
可以把一个选择符附加到另一个选择符,selector-unify()
将两组选择器合成一个复合选择器。
@use 'sass:selector';
@debug selector.is-superselector("a", "a"); // true
// 可以直接使用@forward下的前缀
@debug selector-append("a", ".disabled"); // a.disabled
@debug selector-extend("a.disabled", "a", ".link"); // a.disabled, .link.disabled
.header {
content: selector-append(".a", ".b", ".c") + '';
content: selector-unify("a", ".disabled") + '';
}
编译为
.header {
content: ".a.b.c";
content: "a.disabled";
}
meta
提供一个mixin
和一些原子级别的function
,比如使用meta.calc-args
获取方法的参数,meta.calc-name
获取方法名。
meta.load-css($url,$with:())
该mixin
可以把$url
中css
样式全部包含进来。注意,$url
引入的函数,变量和mixin
在 meta.load-css()
后的scss
中并不能用,它只会返回编译后的css
代码。它的第二个参数可以修改使用了!default
的变量。
src/corners
$border-contrast: false !default;
code {
background-color: #6b717f;
color: #d2e1dd;
@if $border-contrast {
border-color: #dadbdf;
}
}
index.scss
@use "sass:meta";
body.dark {
@include meta.load-css("src/corners", $with: ("border-contrast": true));
}
编译为
body.dark code {
background-color: #6b717f;
color: #d2e1dd;
border-color: #dadbdf;
}
@use "sass:meta";
@debug meta.calc-args(calc(100px + 10%)); // unquote("100px + 10%")
@debug meta.calc-args(clamp(50px, var(--width), 1000px)); // 50px, unquote("var(--width)"), 1000px
@debug meta.calc-name(calc(100px + 10%)); // "calc"
@debug meta.calc-name(clamp(50px, var(--width), 1000px)); // "clamp"
@debug
打印表达式的值,方便调试。
$font-sizes: 10px + 20px;
$style: (
color: #bdc3c7
);
.container {
@debug $style;
@debug $font-sizes;
}
@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
显示警告性建议,会显示堆栈信息。
$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);
}
自检相关函数,属于内置扩展meta
下的方法,feature-exists()
检查当前scss
版本是否存在某个特性,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);
}
}
编译为
.container {
color: #F00;
padding: 0 10px 0 10px;
}
「看了这么久了,辛苦了,不过我也写了很久啦,大佬不妨点个赞再走吧。」
[1]https://link.juejin.cn/?target=https%3A%2F%2Fwww.yuque.com%2Fistao: https://link.juejin.cn/?target=https%3A%2F%2Fwww.yuque.com%2Fistao
[2]https://www.sass.hk/: https://link.juejin.cn/?target=https%3A%2F%2Fwww.sass.hk%2F
[3]https://sass-lang.com/documentation: https://link.juejin.cn/?target=https%3A%2F%2Fsass-lang.com%2Fdocumentation
[4]https://www.sass.hk/css2sass/: https://link.juejin.cn/?target=https%3A%2F%2Fwww.sass.hk%2Fcss2sass%2F
[5]https://www.sassmeister.com/: https://link.juejin.cn/?target=https%3A%2F%2Fwww.sassmeister.com%2F
[6]https://sass-lang.com/documentation/values/functions: https://link.juejin.cn/?target=https%3A%2F%2Fsass-lang.com%2Fdocumentation%2Fvalues%2Ffunctions
[7]https://sass-lang.com/install: https://link.juejin.cn/?target=https%3A%2F%2Fsass-lang.com%2Finstall
[8]https://juejin.cn/post/7048882600189886494: https://juejin.cn/post/7048882600189886494
[9]https://juejin.cn/post/7050360669316579341: https://juejin.cn/post/7050360669316579341
[10]https://juejin.cn/post/7049919311804104717: https://juejin.cn/post/7049919311804104717
[11]https://juejin.cn/post/7041097839711092744: https://juejin.cn/post/7041097839711092744
[12]https://juejin.cn/post/7028865719102079012: https://juejin.cn/post/7028865719102079012
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8