SASS/SCSS一篇全通
SASS/SCSS一篇全通
- 背景介绍
-
- SaSS的两套语法
- SaSS在本地的安装
- SaSS在React项目的安装
- ScSS的基础语法
-
- 1. 变量(variable)
- 2. 嵌套(nesting)
- 3. 模块(modules)
-
- @use VS @import
- 4.扩展/继承(extend/inheritance)
- 5. 混入(mixins)
-
- 混入 vs 扩展/继承
- 应用场景
- 6.数学操作符(operators)
- ScSS的内置函数
-
- 1. 字符串相关函数(string)
- 2. 数字相关函数(numeric)
- 3. 数组相关函数(list)
- 4. 映射函数(map)
背景介绍
SaSS
全称syntactically awesome style sheets(中文翻译为“语法上很棒的样式表”),并且自喻为拥有超能力的CSS
,听着就特别臭美。SaSS
于2007年诞生,是对CSS
的功能扩展,想要帮助开发者更轻松地,更高效的写样式表。
⚠️ 浏览器是无法直接理解SaSS
的,我们需要一个编译器,把SaSS
翻译成CSS
。
SaSS的两套语法
SaSS开发了两种语法:
- 缩进语法 (
Indented Syntax
):用缩进取代了冒号和花括号,文件后缀为*.sass
- ScSS语法(
Superset Syntax
是CSS
的超集),文件后缀为*.scss
。是对CSS
语法的扩充。也就是说,所有符合CSS
语法的样式表也都是具有相同语法意义的SCSS
文件。➡️ 主流
SaSS在本地的安装
与其他包一眼,我们可以用npm
或者brew
来安装:
npm
:npm install -g sass
brew
:brew install sass/sass/sass
两者达到的效果是一样的,都是本地环境的全局安装(在你电脑所以路径都能使用)。安装之后,我们就可以用sass
命令行工具将sass
编译成css
文件了,比如:
sass index.scss index.css
参见[官方文档]{https://sass-lang.com/install}
SaSS在React项目的安装
本地安装时我们一般使用全局安装(global
)。项目中sass
更偏向于局部安装。比如在某个react项目中,我会用:
npm install sass
安装成功后我能在我react项目的package.json
中看到:
ScSS的基础语法
因为scss
是css
的超集,所以所有css
的语法在scss
中都是有效的。在此之外,scss
还提供了以下功能:
1. 变量(variable)
$font-stack: Helvetica, sans-serif;$primary-color: #333;body { font: 100% $font-stack; color: $primary-color;}
⚠️ 现在的css
语法中也有变量了(var(--blue);
)
2. 嵌套(nesting)
特别适用大型项目。嵌套实现了代码的重用,并且大大提高了准确性。
nav { ul { margin: 0; padding: 0; list-style: none; li { display: inline-block; } }}
3. 模块(modules)
模块使不同的scss
文件可以相互引用
// _base.scss$font-stack: Helvetica, sans-serif;$primary-color: #333;body { font: 100% $font-stack; color: $primary-color;}
使用关键词@use
,我们可以在styles.scss
文件中引用_base.scss
,比如某个变量base.$primary-color
// styles.scss@use 'base';.inverse { background-color: base.$primary-color; color: white;}
⚠️注意到
_base.scss
文件名前面的下划线了吗?它的存在是有意义的:
一般来说,Sass 直接编译所有*.scss
文件。 但是,当你导入文件时,你并不希望scss
文件被编译成css
。不然的话,像变量base.$primary-color
之类的东西都没法用了 !
➡️ 像_base.scss
这样的文件被称为 Partials(部分)
⚠️注意到我们使用
_base.scss
时只提了base
吗
Sass 默认对 Partials的使用需要忽视下划线,
@use VS @import
从根本上说,@use
和@import
这个语法都做同样的事情 --> 在另一个模块中加载另一个模块。
@import
使目标文件中的所有内容都可以全局访问。 这使得导入文件的链无穷无尽,很难追踪变量和mixin
的来源。@import
还允许规则重写,并且很难追溯完美的 CSS 中断的原因 ➡️ 这就是SaSS不再推荐使用@import
的原因。@use
规则使我们能够将样式表分解为更实用、更小的部分,并将它们加载到其他样式表中。用法中最重要的就是 Partials
4.扩展/继承(extend/inheritance)
又是一个提高代码重用率的宝宝。
首先,用百分号%
定义要被继承的父样式shared
%shared {font-family: sans-serif;font-size: medium;color: white;}
使用:
// 不对父样式进行任何修改.text {@extend %shared;}// 以下两个选择器继承并扩展了父样式:.warning {@extend %shared;background-color: red;}.success {@extend %shared;background-color: green;}
⚠️ 父样式%xxx
的内容不会 单独 被编译成css
,无论它是否被用到。上面的scss代码编译之后应该长这样:
.text {font-family: sans-serif;font-size: medium;color: white;}.warning {font-family: sans-serif;font-size: medium;color: white;background-color: red; // ⬅️ 扩展}.success {font-family: sans-serif;font-size: medium;color: white;background-color: green; // ⬅️ 扩展}
5. 混入(mixins)
混入的目的是提高代码重用,特别适用于写“主题”。主要与两个关键词相关@mixin
和@include
。
假设我要建一个类似wordpress的网站。我想要我的整个网页有一致的设计和颜色搭配,比如导航栏(navigator
),弹出框(modal
)。首先,定义一个名为myTheme
的mixin
@mixin myTheme { background-color: lightpink; font-size: 25px; font-weight: bold;}
使用mixin:
.navigator { @include myTheme;}// 使用+扩展.modal { @include myTheme; border: 1px solid blue;}
❓问:这个混入和 **扩展/继承(extend/inheritance)**有什么区别,为什么需要他们同时存在?
❗️答:混入更强大,因为它能使用变量
混入 vs 扩展/继承
假设我要建一个类似wordpress的网站,这个网站中应该又很多不同的主题供用户选择。与函数类似,我们先定义一个mixin的模版,名为theme
。并为参数定义默认值
@mixin theme($color: lightpink, $size: 25px) {background-color: $color;font-size: $size;font-weight: bold;}
在使用时,我们对mixin的模版进行传参,重写覆盖默认值:
// 使用原有的默认值.navigator { @include theme;}// 传入新值,覆盖默认值.new-navigator { @include theme($color: darkorange, $size: 20px); border: 1px solid blue;}
应用场景
TODO
除了我们距离用到的网页主题之外,还有一种用法也很常见,那就是vendor prefixes - 用mixins来声明浏览器相关的属性。举例:
@mixin transform($property) { -webkit-transform: $property; -ms-transform: $property; transform: $property;}.myBox { @include transform(rotate(20deg));}
6.数学操作符(operators)
包括加(+
)减(-
)乘(*
)除(math.div()
),和取整(%
)。在使用前要先导入math的包
@use "sass:math";article[role="main"] { width: math.div(600px, 960px) * 100%;}
ScSS的内置函数
1. 字符串相关函数(string)
…用于操作和获取有关字符串的信息。
⚠️ Sass 字符串是从 1 开始数的!
函数 | 描述 | 例子 | 输出结果 |
---|---|---|---|
quote(string) |
添加字符串,并返回结果。 | quote(Hello World) |
“Hello world” |
str-index(string, substring) |
返回字符串中,某子字符串第一次出现的位置。 | str-index("Hello world!", "H") |
1 |
str-insert(string, insert, index) |
在字符串的某指定位置中插入另一个字符串 | str-insert("Hello world!", " wonderful", 6) |
“Hello wonderful world!” |
str-length(string) |
计算字符串的长度 | str-length("Hello world!") |
12 |
str-slice(string, start, end) |
从字符串中提取字符 | str-slice("Hello world!", 2, 5) |
“ello” |
to-lower-case(string) |
返回大写的字符串 | to-lower-case("Hello World!") |
“hello world!” |
to-upper-case(string) |
返回小写的字符串 | to-upper-case("Hello World!") |
“HELLO WORLD!” |
unique-id() |
生成唯一随机生成的字符串 | unique-id() |
tyghefnsv |
unquote(string) |
除去字符串的引号 | unquote("Hello world!") |
Hello world! |
2. 数字相关函数(numeric)
函数 | 描述 | 例子 | 输出结果 |
---|---|---|---|
abs(number) |
返回数字的绝对值。 | abs(-15) |
15 |
ceil(number) |
向上四舍五入取整 | ceil(15.20) |
16 |
comparable(num1, num2) |
查看两个值是否有可比性 | comparable(20mm, 1cm) |
true |
`` | comparable(35px, 2em) |
false | |
floor(number) |
向下四舍五入取整 | floor(15.80) |
15 |
max(number...) |
取最大值 | max(5, 7, 9, 0, -3, -7) |
9 |
min(number...) |
取最小值 | max(5, 7, 9, 0, -3, -7) |
-7 |
percentage(number) |
将数字转换为百分比(将数字乘以 100) | percentage(1.2) |
120 |
random() |
生成 0 到 1 之间的随机数。 | random() |
0.45673 |
random(number) |
生成从1到 number之间的随机整数 | random(5) |
4 |
round(number) |
将数字四舍五入到最接近的整数 | round(15.20) |
15 |
`` | round(15.80) |
16 |
3. 数组相关函数(list)
函数 | 描述 | 例子 | 输出结果 |
---|---|---|---|
append(list, value, [separator]) |
将单个值添加到列表的末尾。(列表的分隔符可以是自动、逗号或空格) | append((a b c), d) |
a b c d |
index(list, value) |
返回列表中值的索引位置。 | index(a b c, b) |
2 |
is-bracketed(list) |
检查列表是否有方括号。 | is-bracketed([a b c]) |
true |
join(list1, list2, [separator, bracketed]) |
将 list2 追加到 list1 的末尾。 | join((a b c), (d e f), comma) |
a, b, c, d, e, f |
length(list) |
返回列表的长度 | `` | |
list-separator(list) |
返回列表分隔符。比如是空格或逗号。 | list-separator(a, b, c) |
“comma“ |
nth(list, n) |
返回列表中第n个元素 | nth(a b c, 3) |
c |
set-nth(list, n, value) |
将第 n 个列表元素设置为指定的值。 | set-nth(a b c, 2, x) |
a x c |
zip(lists) |
将多个列表组合成一个多维列表。 | zip(1px 2px 3px, solid dashed dotted, red green blue) |
1px solid red, 2px dashed green, 3px dotted blue |
4. 映射函数(map)
函数 | 描述 | 例子 | 输出结果 |
---|---|---|---|
map-get(map, key) |
返回映射中指定键的值。 | $font-sizes: ("small": 12px, "normal": 18px, "large": 24px) map-get($font-sizes, "small") |
12px |
map-has-key(map, key) |
检查 map 是否具有指定的键。 | $font-sizes: ("small": 12px, "normal": 18px, "large": 24px) map-has-key($font-sizes, "big") |
false |
map-keys(map) |
返回 map 中所有键的列表。 | $font-sizes: ("small": 12px, "normal": 18px, "large": 24px) map-keys($font-sizes) |
“small”, "normal, “large” |
map-merge(map1, map2) |
将 map2 追加到 map1 的末尾。 | $font-sizes: ("small": 12px, "normal": 18px, "large": 24px) $font-sizes2: ("x-large": 30px, "xx-large": 36px) map-merge($font-sizes, $font-sizes2) |
“small”: 12px, “normal”: 18px, “large”: 24px, “x-large”: 30px, “xx-large”: 36px |
map-remove(map, keys...) |
删除列表中指定的键。 | $font-sizes: ("small": 12px, "normal": 18px, "large": 24px) map-remove($font-sizes, "small") |
(“normal”: 18px, “large”: 24px) |
map-values(map) |
返回列表中所有值 | $font-sizes: ("small": 12px, "normal": 18px, "large": 24px) map-values($font-sizes) |
12px, 18px, 24px |