前言:Sass 这小妖精,凭啥让前端 er 魂牵梦绕?
在前端开发的花花世界里,CSS 预处理器界的顶流 ——Sass(Syntactically Awesome Stylesheets)那可是横空出世的狠角色!它直接把写样式这事儿从 "搬砖" 升级成 "开挖掘机",效率和可维护性直接拉满到外太空!作为一个对网页设计爱得死去活来的开发者,我太懂掌握 Sass 有多香了 —— 简直是给前端人的超能力 Buff!所以我揣着激动的心、颤抖的手,踏上了这场和 Sass 妹妹的恋爱修行,顺便把笔记记下来给兄弟们抄作业~
一、拿捏 Sass 的正确姿势:从入门到装 X
1.1 Sass 的基本语法:骚操作集中营
A、变量:给 CSS 装个 "记忆大脑"
这波操作直接让 CSS 原地封神 —— 终于能像 JS 大佬那样玩变量了!用$符号给值办个 "身份证",想在哪用就在哪用,改一次全文档生效,简直不要太爽!
\$color: #f00; // 给红色办个"户口本"
.container {
  border: 1px solid \$color; // 叫它一声就来
  background: \$color; // 再叫一声还来
}
编译后直接原地变身:
.container {
  border: 1px solid #f00;
  background: #f00;
}
B、嵌套:像剥洋葱一样写样式,爽!
这玩意儿简单到离谱,地球人都知道!就跟 HTML 的父子关系一模一样,一层套一层,再也不用写一堆重复的选择器,简直是强迫症的福音!(具体用法就不啰嗦了,懂的都懂,不懂的抄作业也能会)
C、文件引入:模块化管理,告别 "一锅乱炖"
文件名前面加个_(比如_variables.scss),这文件就成了 "幕后英雄"—— 不单独编译,专给别人当靠山!新版本推荐用@use替代老掉牙的@import(别问为啥,问就是怕全局污染搞事情),还能起个别名,喊起来更顺口~
@use 'variables'; // 召唤变量文件,后缀名可省,嚣张!
body { color: variables.\$primary-color; } // 用命名空间喊它,免得认错人
\$color: red;
@mixin box { padding: 10px; }
// main.scss
@use 'theme' as t; // 给theme起个小名叫t,亲昵\~
.box {
  color: t.\$color; // 喊小名更方便
  @include t.box; // 混合宏也这么喊
}
D、混入(Mixin):CSS 界的 "万能模板"
这东西就是样式界的 "预制菜"—— 提前做好一段 CSS,要用的时候微波炉加热(@include)一下就成!还能传参数,比外卖还贴心!
// 定义一个"居中神器",默认横排,想竖排也行
@mixin flex-center(\$direction: row) {
  display: flex;
  flex-direction: \$direction;
  justify-content: center;
  align-items: center;
}
.container { @include flex-center(column); } // 喊它一声,竖着居中安排上!
// 重点来了!@content这货是"占位符刺客"——调用时塞点代码,它直接原地替换!
// 定义一个响应式"变形金刚"
@mixin responsive(\$breakpoint) {
  @media (\$breakpoint) {
  @content; // 这里会被替换成你传的代码,超灵活!
  }
}
// 调用它,传点样式让它变身
@include responsive(min-width: 768px) {
  .box { width: 50%; float: left; }
  .title { font-size: 20px; }
}
// 编译后直接成了媒体查询大佬:
@media (min-width: 768px) {
  .box { width: 50%; float: left; }
  .title { font-size: 20px; }
}
// 进阶玩法:带条件的"主题切换器"
@mixin theme(\$mode: light) {
  .theme-#{\$mode} {
  @if \$mode == light {
  background: white; color: black; // 白天模式
  } @else {
  background: black; color: white; // 黑夜模式
  }
  @content; // 留个口子让你塞自定义样式,够意思吧?
  }
}
// 调用它,加点私货
@include theme(dark) {
  .btn { border-color: #333; }
  .link { text-decoration: underline; }
}
// 编译后直接生成带私货的暗黑主题:
.theme-dark {
  background: black; color: white;
  .btn { border-color: #333; }
  .link { text-decoration: underline; }
}
E、继承:CSS 界的 "抄作业大师"
这玩意儿跟 JS 的继承一个德性 ——B 继承 A,A 有的 B 全有,还能加自己的新东西!用@extend一声令下,直接把别人的样式扒过来,爽到飞起!
.btn { padding: 8px 16px; } // 基础按钮样式
.btn-primary {
  @extend .btn; // 抄作业!把.btn的样式全拿过来
  background: blue; // 再加个自己的特色
}
// 编译后直接合体:
.btn, .btn-primary { padding: 8px 16px; }
.btn-primary { background: blue; }
F、运算:CSS 也能当计算器,秀!
Sass 这货直接给 CSS 装了个 "计算器",加减乘除取模样样行,数字、颜色、字符串都能算,单位还能自动转换 —— 数学不好的同学也能轻松拿捏(优先级跟数学一样,别慌)!
// 加法 (+):两个数贴贴
\$width: 200px;
\$margin: 10px;
.element { width: \$width + \$margin; } // 结果:210px
// 减法 (-):数字吵架
\$base-font-size: 16px;
\$small-font-size: 14px;
.small-text { font-size: \$base-font-size - \$small-font-size; } // 结果:2px
// 乘法 (\*):数字翻倍玩
\$base-spacing: 8px;
.double-spacing { margin-bottom: \$base-spacing \* 2; } // 结果:16px
// 除法 (/):数字分家
\$base-font-size: 16px;
\$scale-factor: 1.5;
.large-text { font-size: \$base-font-size / \$scale-factor; } // 结果:10.6666666667px
// 取模 (%):数字找余数
\$columns: 12;
\$column-width: 80px;
.grid-item { width: (\$column-width \* 4) % \$columns; } // 结果:4px
// 混合运算:先乘后除,规矩得很
\$base-font-size: 16px;
.text { font-size: (\$base-font-size \* 0.5) / 2; } // 结果:4px
// 四舍五入:强迫症福音
\$base-font-size: 16px;
.rounded-text { font-size: round(\$base-font-size \* 1.5); } // 结果:24px
// 单位魔法:给数字加单位
\$width: 100;
\$height: 200px;
.box { width: \$width + unit(\$height); } // 结果:100px
1.2 Sass 的数据类型:装 X 必备知识点
Sass 的 "数据家族" 人丁兴旺,个个都是狠角色,掌握它们,你就是 CSS 界的 "数据大佬"!
-
数值类型:1、2、13、10px(带不带单位都嚣张)
-
字符串类型:有引号的 "foo"、'bar',没引号的 baz(放荡不羁爱自由)
-
布尔类型:true、false(非黑即白,从不摸鱼)
-
空值:null(存在感为 0,但偶尔很有用)
-
数组(list):用空格或逗号分隔,比如 1px 10px 15px 5px、1px,10px,15px,5px(排队整齐)
-
字典(map):小括号包着键值对,(key1:value1, key2:value2)(查字典专用)
-
颜色类型:blue、#04a012、rgba (0,0,12,0.5)(五彩斑斓的黑都能整)
1.2.1 数值类型:数字界的 "多面手"
整数、小数、带单位的,啥样都有,运算起来贼溜!
\$my-age: 19; // 整数,年轻就是资本
\$your-age: 19.5; // 小数,半岁也算数
\$height: 120px; // 带单位,身高必须精准
1.2.2 字符串类型:文字界的 "戏精"
带不带引号都能活,还能拼接,玩得花!
\$name: 'Tom Bob'; // 带单引号,害羞型
\$container: "top bottom"; // 带双引号,张扬型
\$what: heart; // 没引号,放飞自我型
div {
  background-image: url(\$what + ".png"); // 拼接一下,变成heart.png
}
// 最终结果:
div { background-image: url(heart.png); }
1.2.3 布尔类型:是非分明的 "杠精"
就俩值:true 和 false,还会用 and、or、not 吵架,逻辑清晰得很!
\$a: 1>0 and 0>5; // false(1>0是对的,但0>5是错的,合起来错)
\$b: "a" == a; // true(字符串a和无引号a是一家)
\$c: false; // 就是错
\$d: not \$c; // true(反过来就对了)
1.2.4 null 类型:透明的 "隐身人"
存在感为 0,但有时候能帮大忙(比如控制样式是否生成)。
\$a: null; // 我在,但又没完全在
1.2.5 list 类型:排队整齐的 "小方队"
元素排好队,用空格或逗号分隔,拿来循环超方便!
\$p: 1px 1px 1px 1px; // 四个1px排好队
.div {
  padding: \$p; // 直接用,相当于padding:1px 1px 1px 1px
}
1.2.6 字典类型:查东西超快的 "新华字典"
键值对打包,用 map 函数查起来比翻书还快!
\$map: (name:"张三", age:99, sex:"男"); // 张三的信息打包
\$primary: map-get(\$colors, "name"); // 查name,直接跳出"张三"
// 新版用法,更嚣张
@use "sass:map";
\$font-weights: ("regular": 400, "medium": 500, "bold": 700);
@debug map.get(\$font-weights, "medium"); // 500(一下子就查到)
@debug map.get(\$font-weights, "extra-bold"); // null(查无此货)
1.2.7 颜色类型:色彩界的 "调色大师"
跟 CSS 颜色一样,但 Sass 能对它们动手动脚(调整亮度、饱和度啥的),十六进制、颜色名、rgb/rgba 都能整!
1.3 函数:Sass 的 "超能力外挂"
这玩意儿简直是 Sass 的 "王炸"!配合变量、数组、字典、循环一起玩,能上天!注意:函数也能当值用,高级玩法直接拉满(虽然不能直接写,但能通过内置函数调用,懂的都懂)。
不多逼逼,直接上才艺(把它当 JS 看就行,没难度)!
// 先引入内置模块,跟JS的工具库一个意思
@use "sass:list";
@use "sass:meta";
@use "sass:string";
/// 自定义函数:把列表中符合条件的元素踢出去
@function remove-where(\$list, \$condition) {
  \$new-list: (); // 空列表,准备装新东西
  \$separator: list.separator(\$list); // 记录分隔符类型
  // 循环列表,逐个检查
  @each \$element in \$list {
  @if not meta.call(\$condition, \$element) { // 不符合条件的留下
  \$new-list: list.append(\$new-list, \$element, \$separator: \$separator);
  }
  }
  @return \$new-list; // 返回新列表
}
\$fonts: Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif;
.content {
  @function contains-helvetica(\$string) { // 内部小函数:检查是否含"Helvetica"
  @return string.index(\$string, "Helvetica");
  }
  font-family: remove-where(\$fonts, meta.get-function("contains-helvetica")); // 踢掉含"Helvetica"的字体
}
1.3.1 自定义函数:自己造轮子,想咋玩咋玩
自定义函数就是自己写的 "专属工具",有函数名、@function声明和函数体,跟 JS 函数一模一样,想实现啥功能自己说了算!
// 自定义函数:根据基础色生成随机"亲戚色"
@function random-color(\$base-color) {
  // 从基础色里扒出色相、饱和度、亮度
  \$hue: hue(\$base-color);
  \$saturation: saturation(\$base-color);
  \$lightness: lightness(\$base-color);
  
  // 随机色相偏移(-60到+60之间),既相关又不同
  \$random-hue-offset: random(120) - 60; // 生成-60到60的随机数
  \$new-hue: \$hue + \$random-hue-offset;
  
  // 确保色相在0-360范围内(循环一下,不越界)
  @if \$new-hue < 0 {
  \$new-hue: \$new-hue + 360;
  } @else if \$new-hue > 360 {
  \$new-hue: \$new-hue - 360;
  }
  
  // 随机调整饱和度(±10%),不能太离谱
  \$random-saturation: \$saturation + (random(20) - 10);
  \$new-saturation: clamp(0%, \$random-saturation, 100%); // 锁死在0-100%
  
  // 随机调整亮度(±10%),同样不能太疯
  \$random-lightness: \$lightness + (random(20) - 10);
  \$new-lightness: clamp(0%, \$random-lightness, 100%); // 锁死范围
  
  // 返回新颜色,搞定!
  @return hsl(\$new-hue, \$new-saturation, \$new-lightness);
}
// 实战一下
\$base: #4285f4; // 基础蓝色
// 生成5个"亲戚色"
.color-1 { background: random-color(\$base); }
.color-2 { background: random-color(\$base); }
.color-3 { background: random-color(\$base); }
.color-4 { background: random-color(\$base); }
.color-5 { background: random-color(\$base); }
1.3.2 SASS 内置函数:官方给的 "作弊神器"
Sass 自带一堆函数,处理字符串、数字、数组、字典、颜色啥的,比自己写的好用一万倍,直接拿来主义!
1.3.2.1 字符串内置函数:文字处理小能手
这些函数能给字符串加引号、找子串、插入文字... 比 Word 的查找替换还强!(示例用@debug打印结果,方便调试,不是生成 CSS 哦)
| 函数 | 老版函数名 | 返回值 | 功能描述 |
|---|---|---|---|
string.quote($string) | quote($string) | string | 给字符串加引号,带引号的不变 |
string.index($string, $substring) | str-index($string, $substring) | number | 找子串位置,找不到返回 null |
string.insert($string, $insert, $index) | str-insert($string, $insert, $index) | string | 在指定位置插入子串 |
string.length($string) | str-length($string) | number | 算字符串长度 |
string.slice($string, $start-at, $end-at) | str-slice($string, $start-at, $end-at) | string | 截取字符串,包含首尾 |
string.split($string, $separator, $limit) | - | list | 按分隔符拆分字符串,$limit 限制拆分次数 |
string.to-upper-case($string) | to-upper-case($string) | string | 转大写(仅 ASCII 字母) |
string.to-lower-case($string) | to-lower-case($string) | string | 转小写(仅 ASCII 字母) |
string.unique-id() | - | string | 生成唯一 ID,每次都不一样 |
string.unquote($string) | unquote($string) | string | 移除引号,只移外层 |
@use "sass:string";
// 1. 给字符串加引号,仪式感拉满
@debug string.quote(Open Sans); // 输出:"Open Sans"(没引号的加上)
@debug string.quote("Roboto"); // 输出:"Roboto"(有引号的不动)
// 2. 找子串位置,跟找对象一样准
@debug string.index("Hello World", "W"); // 输出:7(W在第7位)
@debug string.index("sass is cool", "is"); // 输出:6(is从第6位开始)
@debug string.index("abcdef", "x"); // 输出:null(查无此串)
// 3. 插入子串,想插哪就插哪
@debug string.insert("Hello", " beautiful", 6); // 输出:"Hello beautiful"(末尾加)
@debug string.insert("abc123", "-", 4); // 输出:"abc-123"(第4位插横线)
@debug string.insert("test", "pre-", -10); // 输出:"pre-test"(索引太小就放开头)
// 4. 算长度,一个字符都不放过
@debug string.length("CSS"); // 输出:3(3个字符)
@debug string.length("Sass Script"); // 输出:11(11个字符,空格也算)
@debug string.length(""); // 输出:0(空字符串,没脾气)
// 5. 截取字符串,想留哪段留哪段
@debug string.slice("Programming", 5); // 输出:"amming"(从第5位到末尾)
@debug string.slice("123456789", 2, 5); // 输出:"2345"(第2到第5位)
@debug string.slice("abcdefgh", 1, -3); // 输出:"abcde"(开头到倒数第3位前)
// 6. 拆分字符串,按规则分家
@debug string.split("a,b,c,d", ","); // 输出:\["a", "b", "c", "d"](按逗号拆)
@debug string.split("2024-08-12", "-", 1); // 输出:\["2024", "08-12"](只拆1次)
@debug string.split("foo|bar|baz", "|", 2); // 输出:\["foo", "bar", "baz"](拆2次够了)
// 7. 转大写,气场全开
@debug string.to-upper-case("sass"); // 输出:"SASS"(小写变大写)
@debug string.to-upper-case("Hello World"); // 输出:"HELLO WORLD"(单词全大写)
// 8. 转小写,低调做人
@debug string.to-lower-case("CSS3"); // 输出:"css3"(大写变小写)
@debug string.to-lower-case("HELLO"); // 输出:"hello"(全变小写)
// 9. 生成唯一ID,永不重复
@debug string.unique-id(); // 输出:u1a2b3c4d(随机一串)
@debug string.unique-id(); // 输出:u5e6f7g8h(每次都不一样)
// 10. 移除引号,放飞自我
@debug string.unquote("Arial"); // 输出:Arial(引号没了)
@debug string.unquote('"quoted"'); // 输出:"quoted"(只移外层引号)
1.3.2.2 数字内置函数:数学不好也能玩转
加减乘除、取整、比较大小... 这些函数比计算器还好用,再也不用手算样式值了!
| 函数 | 返回值 | 功能描述 |
|---|---|---|
math.abs($number) | number | 取绝对值 |
math.ceil($number) | number | 向上取整(往大了整) |
math.floor($number) | number | 向下取整(往小了整) |
math.round($number) | number | 四舍五入 |
math.max($numbers...) | number | 找最大值 |
math.min($numbers...) | number | 找最小值 |
math.pow($base, $exponent) | number | 算乘方(base 的 exponent 次方) |
math.sqrt($number) | number | 算平方根 |
math.rad($angle) | number | 角度转弧度 |
math.deg($angle) | number | 弧度转角度 |
math.random() | number | 生成 0-1 随机浮点数 |
math.clamp($value, $min, $max) | number | 把值锁在 min 和 max 之间(超了就取边界) |
math.sign($number) | number | 返回符号(正数 1,负数 - 1,0 返回 0) |
@use "sass:math";
// 1. 基础数值处理,正负大小全搞定
\$num: -3.76;
.basic-ops {
  absolute: math.abs(\$num); // 3.76(绝对值,负变正)
  rounded: math.round(\$num); // -4(四舍五入,够意思)
  ceiling: math.ceil(\$num); // -3(向上取整,往大了整)
  flooring: math.floor(\$num); // -4(向下取整,往小了整)
  sign: math.sign(\$num); // -1(返回符号,负数就是-1)
}
// 2. 范围限制,再也不怕值太离谱
\$value: 15;
.clamp-example {
  within-range: math.clamp(\$value, 10, 20); // 15(在范围内,就用它)
  below-min: math.clamp(5, 10, 20); // 10(低于最小,就用10)
  above-max: math.clamp(25, 10, 20); // 20(高于最大,就用20)
}
// 3. 多值比较,谁大谁小一眼看穿
\$values: 8.5, 3.2, 12, -4, 7.9;
.extremes {
  maximum: math.max(\$values...); // 12(最大值,C位出道)
  minimum: math.min(\$values...); // -4(最小值,垫底)
}
// 4. 高级数学运算,装X专用
.calculations {
  power: math.pow(2, 10); // 1024(2的10次方,1KB=1024B懂吧)
  root: math.sqrt(256); // 16(16x16=256,没毛病)
  fractional: math.pow(8, 1/3); // 2(8的立方根,2x2x2=8)
}
// 5. 角度转换,transform动画必备
.angle-ops {
  radians: math.rad(270deg); // 4.71238898rad(270度转弧度)
  degrees: math.deg(math.pi()/2); // 90deg(π/2弧度转角度,刚好直角)
}
// 6. 随机数生成,抽奖就靠它
.random-values {
  basic: math.random(); // 0-1之间的随机数(如0.6429)
  range: math.floor(math.random() \* 100); // 0-99之间的随机整数(如37)
}
1.3.2.3 数组内置函数:列表操作小能手
数组(list)在 Sass 里超常用,这些函数能增删改查、合并拆分,比 JS 数组方法还顺手!
| 函数 | 功能描述 |
|---|---|
list.append($list, $value, $separator: auto) | 往列表末尾加元素 |
list.index($list, $value) | 找元素位置,找不到返回 null |
list.join($list1, $list2, $separator: auto) | 合并两个列表 |
list.length($list) | 算列表长度(元素个数) |
list.nth($list, $n) | 取第 n 个元素(索引从 1 开始,别搞错了) |
list.separator($list) | 看列表用空格还是逗号分隔 |
list.set-nth($list, $n, $value) | 替换第 n 个元素 |
list.slice($list, $start-at, $end-at: -1) | 截取列表的一部分 |
@use "sass:list";
// 定义两个列表,一个空格分,一个逗号分
\$spacing: 8px 16px 24px 32px; // 空格分隔,兄弟情深
\$sizes: 12px, 14px, 16px, 18px; // 逗号分隔,保持距离
// 1. 看长度、取元素,了如指掌
.length-nth {
  spacing-length: list.length(\$spacing); // 4(4个元素,一个不少)
  second-size: list.nth(\$sizes, 2); // 14px(第2个元素,就是它)
  last-spacing: list.nth(\$spacing, -1); // 32px(负索引,倒数第一个)
}
// 2. 找元素位置,跟GPS定位一样准
.index-check {
  sixteen-index: list.index(\$sizes, 16px); // 3(16px在第3位)
  missing-index: list.index(\$spacing, 20px); // null(这货不在列表里)
}
// 3. 加元素、换元素,想改就改
.modify-list {
  added-size: list.append(\$sizes, 20px); // 12px, 14px, 16px, 18px, 20px(加个20px)
  replaced-spacing: list.set-nth(\$spacing, 3, 28px); // 8px 16px 28px 32px(第3个换成28px)
}
// 4. 合并列表,搞个大新闻
.combine-lists {
  joined: list.join(\$spacing, \$sizes); // 8px 16px 24px 32px, 12px, 14px, 16px, 18px(默认保持分隔符)
  forced-separator: list.join(\$spacing, \$sizes, \$separator: space); // 全用空格分隔,不分彼此
}
// 5. 截取列表,想要哪段就哪段
.sliced-lists {
  first-two: list.slice(\$spacing, 1, 2); // 8px 16px(前两个,打包带走)
  from-third: list.slice(\$sizes, 3); // 16px, 18px(从第3个到最后)
  exclude-last: list.slice(\$spacing, 1, -2); // 8px 16px(排除最后两个)
}
// 6. 实战:循环生成类名,批量干活
@each \$index, \$size in \$sizes {
  .text-#{\$index} {
  font-size: \$size; // 生成.text-1到.text-4,分别对应不同大小
  }
}
1.3.2.4 字典内置函数:键值对管理大师
字典(map)就是键值对的集合,这些函数能查值、删键、合并,比查字典还快!
| 函数 | 功能描述 |
|---|---|
map.get($map, $key) | 按键取值,取不到返回 null |
map.has-key($map, $key) | 检查键是否存在,返回 true/false |
map.keys($map) | 取所有键,组成列表 |
map.merge($map1, $map2) | 合并两个字典,重复的键后者覆盖前者 |
map.remove($map, $keys...) | 删掉指定的键和值 |
map.set($map, $key, $value) | 新增或修改键值对 |
map.values($map) | 取所有值,组成列表 |
@use "sass:map";
@use "sass:list";
// 定义个主题配置字典,啥都有
\$theme: (
  primary: #2563eb,
  secondary: #64748b,
  accent: #f97316,
  sizes: (
  sm: 0.8rem,
  md: 1rem,
  lg: 1.25rem
  )
);
// 1. 取值、查键,基础操作
.basic-operations {
  primary-color: map.get(\$theme, primary); // #2563eb(取primary的值)
  has-accent: map.has-key(\$theme, accent); // true(accent存在,没骗你)
  has-warning: map.has-key(\$theme, warning); // false(warning不存在,别瞎找)
  medium-size: map.get(\$theme, sizes, md); // 1rem(嵌套字典取值,md大小)
}
// 2. 取所有键、所有值,一网打尽
.keys-values {
  all-keys: map.keys(\$theme); // primary, secondary, accent, sizes(所有键都在这)
  color-values: map.values(map.remove(\$theme, sizes)); // 所有颜色值,排除sizes
}
// 3. 修改、合并字典,想咋改咋改
.modify-map {
  with-warning: map.set(\$theme, warning, #ef4444); // 加个warning键,值是红色
  merged-theme: map.merge(\$theme, (
  primary: #3b82f6, // 覆盖原primary
  dark: #1e293b // 加个新键dark
  ));
}
// 4. 删键、循环,批量生成样式
\$colors: map.remove(\$theme, sizes); // 删掉sizes,只留颜色
// 循环颜色,生成文本颜色类
@each \$name, \$color in \$colors {
  .text-#{\$name} {
  color: \$color;
  }
}
// 循环尺寸,生成字体大小类
@each \$name, \$size in map.get(\$theme, sizes) {
  .text-#{\$name} {
  font-size: \$size;
  }
}
1.3.2.5 颜色内置函数:调色大师的秘密武器
这些函数能调亮度、饱和度、透明度,还能混合颜色,比 PS 调色还方便,设计师看了都直呼内行!
| 函数 | 功能描述 |
|---|---|
color.adjust($color, $props...) | 相对调整颜色属性(如亮度 + 10%) |
color.alpha($color) | 取透明度(0-1 之间) |
color.change($color, $props...) | 绝对设置颜色属性(如色相固定为 120deg) |
color.lighten($color, $amount) | 提亮颜色(变浅) |
color.darken($color, $amount) | 变暗颜色(变深) |
color.saturate($color, $amount) | 提高饱和度(更鲜艳) |
color.desaturate($color, $amount) | 降低饱和度(更灰暗) |
color.mix($color1, $color2, $weight: 50%) | 混合两种颜色,$weight 控制第一种占比 |
color.to-hsl($color) | 转成 HSL 格式字典(含色相、饱和度等) |
color.to-rgb($color) | 转成 RGB 格式字典(含红、绿、蓝通道) |
color.rgb($r, $g, $b, $alpha: 1) | 用 RGB 值创建颜色 |
color.hsl($h, $s, $l, $alpha: 1) | 用 HSL 值创建颜色 |
@use "sass:color";
@use "sass:map";
// 基础颜色,就它俩了
\$primary: #4f46e5; // 靛蓝色,高冷范
\$secondary: #ec4899; // 粉红色,小清新
// 1. 调亮度、饱和度,换个心情
.lightness-saturation {
  lightened: color.lighten(\$primary, 30%); // 亮30%,阳光点
  darkened: color.darken(\$primary, 20%); // 暗20%,沉稳点
  saturated: color.saturate(\$secondary, 25%); // 艳25%,更骚气
  desaturated: color.desaturate(\$secondary, 40%); // 灰40%,低调点
}
// 2. 调透明度、改属性,随心变
.alpha-properties {
  half-transparent: color.change(\$primary, \$alpha: 0.5); // 半透明,朦胧美
  full-transparent: color.adjust(\$secondary, \$alpha: -0.8); // 透80%,快看不见了
  hue-changed: color.change(\$primary, \$hue: 120deg); // 色相120度,变绿色
}
// 3. 混合颜色,生个"混血儿"
.color-mixing {
  equal-mix: color.mix(\$primary, \$secondary); // 50:50混合,各占一半
  primary-heavy: color.mix(\$primary, \$secondary, 70%); // 主色70%,占主导
  secondary-heavy: color.mix(\$primary, \$secondary, 20%); // 辅助色80%,抢风头
}
// 4. 提取颜色信息,查户口专用
.color-info {
  primary-alpha: color.alpha(\$primary); // 1.0(完全不透明,很实在)
  secondary-rgb: color.to-rgb(\$secondary); // RGB通道信息,红、绿、蓝各多少
  secondary-red: map.get(color.to-rgb(\$secondary), red); // 单拿红色通道值
  primary-hsl: color.to-hsl(\$primary); // HSL信息,色相、饱和度、亮度
  primary-hue: map.get(color.to-hsl(\$primary), hue); // 单拿色相值
}
// 5. 自己造颜色,DIY专属色
.color-creation {
  custom-rgb: color.rgb(79, 70, 229); // 用RGB值造,和\$primary一样
  custom-hsl: color.hsl(243deg, 86%, 55%); // 用HSL值造,也和\$primary一样
  transparent-rgb: color.rgb(236, 72, 153, 0.6); // 带透明度的RGB色
}
// 6. 实战:生成渐变色阶,一套颜色全家桶
\$base: #10b981; // 基础绿色
@for \$i from 1 through 5 {
  .color-#{\$i}00 {
  background-color: color.lighten(\$base, (5 - \$i) \* 15%); // 从深到浅,5个色阶
  }
}
1.3.2.6 其他内置函数:选择器操作小技巧
这些函数能玩选择器,嵌套、合并、加后缀,写组件样式超方便!
| 函数 | 功能描述 |
|---|---|
selector.append($selector, $suffix) | 给选择器加后缀(如.btn + :hover → .btn:hover) |
selector.extend($selector, $extendee) | 模拟 @extend,扩展选择器 |
selector.nest($selectors...) | 嵌套选择器(如.parent 和 &:hover → .parent:hover) |
selector.parse($selector) | 把选择器字符串转成列表 |
selector.unify($selector1, $selector2) | 合并两个选择器(如.a 和.b → .a.b) |
@use "sass:selector";
// 1. 嵌套选择器,父子关系一键搞定
.nesting {
  nested: selector.nest(".parent", "&:hover .child"); 
  // 结果:.parent:hover .child(完美嵌套)
}
// 2. 给选择器加后缀,状态轻松加
.suffix {
  hover-state: selector.append(".btn", ":hover"); 
  // 结果:.btn:hover(hover状态安排上)
  active-state: selector.append("input", "\[type='button']"); 
  // 结果:input\[type='button'](按钮类型input)
}
// 3. 合并选择器,强强联合
.unifying {
  combined: selector.unify(".btn", ".primary"); 
  // 结果:.btn.primary(既是btn又是primary)
  element-combined: selector.unify("a", ".active"); 
  // 结果:a.active(激活状态的链接)
}
// 4. 实战:批量生成禁用样式,效率拉满
\$components: "btn", "card", "modal";
@each \$comp in \$components {
  \#{selector.append(".#{\$comp}", "--disabled")} { // 生成.btn--disabled等
  opacity: 0.6;
  pointer-events: none; // 禁用点击
  }
}
1.4 控制语句:Sass 的 "指挥系统"
跟 JS 一样,Sass 也有条件判断和循环,能根据情况生成不同样式,简直是 "智能生成机"!
1.4.1 @if 条件控制:看情况办事,不瞎搞
跟 JS 的 if 一模一样,单分支、双分支、多分支,还有三目运算,灵活得很!
- 三目运算:一行搞定简单判断
.container {
  color: if(1+1>2, red, green); // 1+1不大于2,所以是green
}
.view {
  color: if(1px + 2px == 3px, yellow, blue); // 1+2=3,所以是yellow
}
// 编译结果:
.container { color: green; }
.view { color: yellow; }
- @if 单分支:符合条件才执行
.app {
  @if 1+1 == 2 { color: red; } // 条件成立,执行
  @if 1+2 == 6 { color: blue; } // 条件不成立,跳过
  margin: 10px; // 不管怎样都执行
}
// 编译结果:
.app { color: red; margin: 10px; }
- @if...@else 双分支:二选一
p {
  @if 1+1 == 2 { color: red; } // 对的,用red
  @else { color: blue; }
  margin: 10px;
}
div {
  @if 1+1 == 3 { color: red; } // 错的,不用red
  @else { color: blue; }
  margin: 10px;
}
// 编译结果:
p { color: red; margin: 10px; }
div { color: blue; margin: 10px; }
- @if...@else if 多分支:多个条件选一个
\$type: monster; // 变量是monster
p {
  @if \$type == ocean { color: blue; } // 不是ocean,跳过
  @else if \$type == matador { color: red; } // 不是matador,跳过
  @else if \$type == monster { color: green; } // 是monster,用green
  @else { color: black; } // 都不是才用black
}
// 编译结果:
p { color: green; }
1.4.2 循环语句:重复的活交给机器干
跟 JS 的循环一样,有 for、each、while,批量生成样式贼快,再也不用复制粘贴了!
- @for 循环:按次数来,精准控制
两种写法:through包含结束值,to不包含结束值,选哪个看心情!
// @for \$var from \<start> to \<end> → 不包含end
@for \$i from 1 to 3 {
  .item-#{\$i} { width: \$i \* 2px; } // i=1、2,生成.item-1和.item-2
}
// @for \$var from \<start> through \<end> → 包含end
@for \$i from 1 through 3 {
  .item2-#{\$i} { width: \$i \* 2em; } // i=1、2、3,生成三个类
}
- @each 循环:遍历列表或字典,逐个处理
跟 JS 的 for...of 差不多,列表、字典都能遍历,超好用!
// 遍历列表
\$arr: puma, sea-slug, egret, salamander;
@each \$animal in \$arr { // 逐个拿动物名
  .#{\$animal}-icon {
  background: url("/asstes/#{\$animal}.png"); // 生成对应图片路径
  }
}
// 遍历字典
\$dict: (
  h1: 2em,
  h2: 1.5em,
  h3: 1.2em,
  h4: 1em,
);
@each \$key, \$value in \$dict { // 逐个拿键和值
  \#{\$key} { font-size: \$value; } // 生成h1到h4的字体大小
}
- @while 循环:满足条件就一直干
跟 JS 的 while 一样,条件成立就循环,小心死循环哦!
\$i: 6; // 初始值6
@while \$i > 0 { // 只要i>0就继续
  .item-#{\$i} { width: 2em \* \$i; } // 生成样式
  \$i: \$i - 2; // i减2,避免死循环
}
1.5 SASS 中的 @规则:特殊指令,各有神通
Sass 扩展了 CSS 的 @规则,功能更强大,用法更骚气!
1.5.1 @import:老古董但还能用
这是老版本的导入语法,虽然现在推荐用 @use,但还是得了解一下它的骚操作!
核心功能:
-
万物皆可复用:不仅能导入 CSS,还能直接用被导入文件的变量、混合宏、函数,代码复用拉满!
-
编译时合并:跟原生 CSS 的 @import(浏览器加载时才请求)不一样,Sass 的 @import 在编译时就把文件合并成一个,减少请求,速度飞起!
语法规则:
- 基础用法:导入时可以省扩展名(.scss、.sass、.css 都行)
// 导入同目录的\_variables.scss(下划线文件是"局部文件",不单独编译)
@import 'variables';
// 导入CSS文件也没问题
@import 'reset.css';
- 批量导入:用逗号分隔,一次导入多个,不用重复写 @import
// 一次性导入reset、variables、button三个文件,效率高
@import 'reset', 'variables', 'components/button';
- 缩进语法(.sass)专属:.sass 文件里导入可以不加引号,更简洁
// style.sass
@import variables // 不用引号,直接写
body
  color: \$primary // 用导入的变量,爽
与原生 CSS @import 的区别:
| 特性 | 原生 CSS @import | Sass @import |
|---|---|---|
| 处理时机 | 浏览器渲染时才加载 | 编译时就合并成一个文件 |
| 性能影响 | 多文件多请求,慢! | 合并成一个文件,快! |
| 可导入内容 | 只能导 CSS 样式 | 能导变量、混合宏、函数,啥都能复用 |
| 语法灵活性 | 每次导入都要写 @import | 逗号分隔多文件,一次搞定 |
1.5.2 @use:现代模块化扛把子
@use是 Sass 的新宠,专门替代@import,解决全局污染问题,模块化管理贼爽!
核心功能:
-
命名空间隔离:导入的模块内容都在自己的命名空间里,用 "模块名。内容" 访问,再也不怕变量重名打架了!
-
私有默认:模块里的东西默认私有,只通过命名空间暴露,全局干干净净,强迫症狂喜!
基本语法:
用@use '文件路径'导入,通过 "模块名。内容" 访问变量、混合宏、函数,清晰明了!
// \_variables.scss(局部模块,藏起来)
\$primary: #2563eb;
\$font-size: 16px;
// style.scss(主文件)
@use 'variables'; // 导入模块,默认命名空间是文件名
.box {
  color: variables.\$primary; // 用命名空间访问变量,不会错
  font-size: variables.\$font-size;
}
1.5.3 @media:响应式布局的神助攻
原生 CSS 的 @media 是媒体查询,Sass 直接把它强化了,更好用!
- 允许嵌套在选择器里:不用单独写在外面,结构更清晰
.navigation {
  display: flex;
  justify-content: flex-end;
  @media (max-width: 768px) { // 嵌套在.navigation里
  flex-direction: column; // 小屏幕就竖着排
  }
}
// 编译后自动提出来:
.navigation {
  display: flex;
  justify-content: flex-end;
}
@media (max-width: 768px) {
  .navigation { flex-direction: column; }
}
- 能用上变量:断点值用变量存,改起来方便,不用到处找
\$mobile-breakpoint: 768px; // 手机断点存成变量
.navigation {
  display: flex;
  justify-content: flex-end;
  @media (max-width: \$mobile-breakpoint) { // 直接用变量,爽
  flex-direction: column;
  }
}
// 编译结果:
.navigation {
  display: flex;
  justify-content: flex-end;
}
@media (max-width: 768px) {
  .navigation { flex-direction: column; }
}
- 还能结合混合宏:通过混合宏可以将响应式逻辑进行封装,实现代码的复用,提升开发效率。
@mixin respond-to(\$breakpoint) {
  @if \$breakpoint == "mobile" {
  @media (max-width: 768px) {
  @content;
  }
  } @else if \$breakpoint == "tablet" {
  @media (min-width: 769px) and (max-width: 1024px) {
  @content;
  }
  } @else if \$breakpoint == "desktop" {
  @media (min-width: 1025px) {
  @content;
  }
  }
}
.container{
  width: 80%;
  @include respond-to("mobile"){
  width: 100%;
  }
  @include respond-to("desktop"){
  width: 70%;
  }
}
等同于传统 CSS 代码:
.container {
  width: 80%;
}
@media (max-width: 768px) {
  .container {
  width: 100%;
  }
}
@media (min-width: 1025px) {
  .container {
  width: 70%;
  }
}
@extend
在编写 CSS 样式时,常常会遇到多个元素的样式存在大量共性,仅部分样式有所差异的情况。此时,就可以借助 Sass 的@extend规则实现样式继承,让一个选择器能够复用另一个选择器的样式规则。
.button {
  display: inline-block;
  padding: 20px;
  background-color: red;
  color: white;
}
.primary-button{
  @extend .button;
  background-color: blue;
}
上述 Sass 代码编译后的 CSS 如下:
.button, .primary-button {
  display: inline-block;
  padding: 20px;
  background-color: red;
  color: white;
}
.primary-button {
  background-color: blue;
}
对于初学者来说,可能会混淆@extend与@mixin,二者虽然都能提取公共样式,但存在本质区别:
-
参数支持:
@mixin支持传递参数,灵活性更强;@extend不支持参数传递。 -
生成的 CSS:
@extend会合并选择器,生成的 CSS 更紧凑,继承的样式真实存在于最终代码中;@mixin会在每个@include处完整生成 CSS 代码,本质是简单的代码替换。 -
使用场景:
@extend适用于继承已有样式,如 UI 框架的通用样式;@mixin适用于需自定义参数的场景,如为不同组件生成相似样式。
再看一个复杂示例:
.box {
  border: 1px #f00;
  background-color: #fdd;
}
.container {
  @extend .box;
  border-width: 3px;
}
.box.a{
  background-image: url("/image/abc.png");
}
编译后的 CSS 为:
.box, .container {
  border: 1px #f00;
  background-color: #fdd;
}
.container {
  border-width: 3px;
}
.box.a, .a.container {
  background-image: url("/image/abc.png");
}
在这个例子中,.container继承了.box的所有样式。若某个元素需同时具备.box和.a类才能应用特定样式(如abc样式),那么带有.container和.a类的元素,同样会应用该样式。
此外,当需要定义一套仅用于继承、不希望单独编译输出的样式时,可以使用%作为占位符:
%button {
  display: inline-block;
  padding: 20px;
  background-color: red;
  color: white;
}
.primary-button{
  @extend %button;
  background-color: blue;
}
.secondary-button{
  @extend %button;
  background-color: pink;
}
编译后的 CSS:
.secondary-button, .primary-button {
  display: inline-block;
  padding: 20px;
  background-color: red;
  color: white;
}
.primary-button {
  background-color: blue;
}
.secondary-button {
  background-color: pink;
}
@at-root
当需要将嵌套规则移动到根级别(声明时不在根级别)时,可使用@at-root。
.parent{
  color: red;
  @at-root .child{
  color: blue;
  }
}
编译后的 CSS:
.parent {
  color: red;
}
.child {
  color: blue;
}
若要移动一组规则,需在@at-root后添加大括号,将目标样式组包含其中:
.parent {
  color: red;
  @at-root {
  .child {
  color: blue;
  }
  .test {
  color: pink;
  }
  .test2 {
  color: purple;
  }
  }
}
编译后:
.parent {
  color: red;
}
.child {
  color: blue;
}
.test {
  color: pink;
}
.test2 {
  color: purple;
}
@debug、@warn、@error
这三个规则用于调试,能在编译过程中输出信息,帮助排查和诊断代码问题。
一、当前 Sass 的核心升级与最佳实践
- 全面转向模块化体系
-
@import 废弃与 @use/@forward 普及:自 2025 年起,Sass 官方已将
@import标记为废弃语法,强烈推荐使用@use和@forward构建模块化体系。-
@use:通过命名空间(如
@use 'variables' as v)隔离作用域,避免全局变量冲突,还支持使用with规则动态配置模块参数。 -
@forward:作为模块导出的中间环节,库作者可借此隐藏内部实现细节,仅暴露公共 API(如
@forward 'theme' show primary-color)。
-
-
Dart Sass 成为唯一官方实现:LibSass 和 Node Sass 已被正式废弃。Dart Sass 通过嵌入式协议解决了跨语言调用和性能问题,支持 C++、Python 等语言通过子进程通信调用,编译速度与原生实现相当。
- 内置模块系统与语法革新
- 函数命名空间化:所有内置函数(如颜色、数学运算函数)均需通过模块调用。例如:
@use 'sass:color';
@use 'sass:math';
\$dark-color: color.scale(\$primary, \$lightness: -10%); // 替代旧的scale-color()
\$result: math.div(10px, 2); // 替代/运算符,避免与CSS分隔符冲突
-
严格的类型检查与语法规范:
-
颜色函数对参数类型要求更严格(如
hsl()必须传入 0-360 的角度值)。 -
列表和映射函数(如
map-get)的参数类型检查增强,减少运行时错误。
-
- 性能与工具链整合
-
编译速度提升:Dart Sass 借助 JIT 编译和内存优化,在大型项目中的编译速度比 Node Sass 快 30% 以上,且支持增量编译。
-
深度集成现代构建工具:
-
Vite 6 + 默认使用 Sass 现代 API,支持
@use模块热更新。 -
Webpack 6 通过
sass-loader实现与 Sass JavaScript API 的无缝对接,支持自定义函数和导入器。
-
二、未来 Sass 的发展方向
- 全面拥抱 CSS 原生生态
-
更紧密的 CSS 兼容性:未来版本将优先支持 CSS 最新特性(如容器查询、嵌套规则),并通过 Sass 模块系统提供更便捷的封装方式。例如,Sass 可能推出
@container混合宏,简化复杂容器查询的编写。 -
与 CSS Houdini 的协同:尽管目前尚未明确支持,但 Sass 团队已表示将探索与 CSS Houdini 的整合,未来开发者或许能通过 Sass 函数动态生成 CSS 自定义属性(如
--custom-var: sass:math.random(100))。
- 类型安全与开发体验升级
-
官方类型系统探索:社区工具
typed-scss-modules已实现 SCSS 类名的 TypeScript 类型推导,未来 Sass 可能内置基础类型检查功能(如变量类型标注、函数参数校验),降低运行时错误发生概率。 -
智能提示与代码补全:借助语言服务器协议(LSP),IDE 将能够识别模块导出的变量和函数,提供更精准的自动补全功能(如 VS Code 已支持模块路径智能提示)。
- 性能与跨平台优化
-
嵌入式协议的全面落地:Dart Sass 的嵌入式协议将支持更多语言(如 Rust、Go),开发者无需依赖 Node.js 环境即可调用 Sass 编译器,提升服务器端渲染(SSR)场景下的效率。
-
并行编译与缓存机制:未来版本将优化多文件编译的并行处理逻辑,通过文件指纹缓存技术减少重复编译时间,尤其在微前端架构中大幅提升构建速度。
- 工具链与生态系统扩展
-
低代码与可视化工具集成:设计工具(如 Figma、Sketch)可能通过插件直接生成 Sass 模块代码,支持动态主题切换和样式变体管理。
-
与 CSS-in-JS 的融合:尽管 Sass 与 CSS-in-JS(如 Styled-components)定位不同,但未来可能通过 Babel 插件实现两者的语法互转,满足混合开发场景需求。
三、开发者应对策略
- 立即行动的迁移步骤
- 存量项目改造:使用
@use逐步替换@import,并通过@forward重构模块依赖关系。例如:
// 旧代码
@import 'variables';
// 新代码
@use 'variables' as v;
.box { color: v.\$primary; }
- 工具链升级:确保构建工具(如 Webpack、Vite)使用最新 Sass 加载器,并配置
api: 'modern'以启用模块化支持。
- 未来技术栈布局建议
-
关注 Dart Sass 的新特性:定期查阅Dart Sass 更新日志,优先尝试嵌入式协议和自定义函数等高级功能。
-
探索社区生态工具:
-
使用
purgecss清理未使用的 CSS,结合 Sass 模块化减少冗余代码。 -
使用
scss-lint或stylelint进行代码质量检查,确保符合模块化规范。
-
四、总结
Sass 正从单纯的 CSS 预处理器逐步进化为现代样式工程平台:
-
现在:通过模块化、类型安全和性能优化,有效解决大型项目的样式维护难题。
-
未来:与 CSS 原生特性、工具链深度整合,成为全链路样式开发的核心基础设施。
开发者应尽早采用@use/@forward体系,充分发挥 Dart Sass 的工具链优势,并关注社区在类型系统和跨平台编译方面的创新,以保持技术栈的竞争力。
(注:文档部分内容可能由 AI 生成)