模块与控制流:@use、内置函数、@if/@for/@each
本文包含 Sass 的三块进阶能力:模块系统(@use / @import)、内置模块与自定义函数、控制指令(@if/@for/@each/@while)。基础语法见 语法基础;复用机制见 复用机制。
一、模块化(@use vs @import)
Dart Sass 1.23+ 起推荐使用 @use / @forward 替代 @import,避免全局污染与重复加载。@import 已标记为弃用,见 Sass 官方说明。
1. 局部文件(Partials)命名规则
以下划线 _ 开头的 Sass 文件称为“局部文件”(Partial)。
- 作用:局部文件不会被单独编译成独立的
.css文件,只有通过@use或@import被引用时,其内容才会合并到主文件中。 - 命名要求:下划线必须在文件名最前面才被 Sass 识别为 Partial。例如
_variables.scss✅;_btn.scss✅;_btn_group.scss✅(下划线在开头即可,后面可再含下划线);btn_group.scss❌(开头没有下划线,会被当作独立根文件编译)。
推荐目录结构:
scss/
├── main.scss # 入口文件,负责 @use 所有模块
├── _variables.scss # Partial:只存变量(无样式规则)
├── _mixins.scss # Partial:Mixin 定义
├── _functions.scss # Partial:自定义函数
└── components/
├── _button.scss
└── _card.scss
2. @use 用法(现代模块系统)
@use 是 Sass 官方推荐的模块加载方式,从 Dart Sass 1.23 开始稳定支持。
基本加载与命名空间
// _variables.scss
$primary-color: #3498db;
$border-radius: 4px;
// main.scss
@use "variables"; // 引入时省略下划线和扩展名
.button {
background: variables.$primary-color; // 必须通过命名空间访问
border-radius: variables.$border-radius;
}
- 命名空间默认为文件名(不含
_和扩展名),如variables。 - 有效避免了不同文件间的变量/函数命名冲突。
自定义命名空间
@use "variables" as vars;
.button {
background: vars.$primary-color;
}
一次性导入所有成员到当前作用域(不推荐)
@use "variables" as *; // 所有成员直接暴露,无需前缀
.button {
background: $primary-color; // 命名冲突风险高
}
不推荐,因为会失去模块化的优势,依然可能导致全局污染。
同时加载多个模块
@use "variables";
@use "mixins";
@use "components/button" as btn;
.element {
@include btn.style; // 调用 mixin
color: variables.$text-color;
}
配置模块变量(需使用 !default)
// _theme.scss
$primary: blue !default;
$danger: red !default;
// main.scss
@use "theme" with (
$primary: #2ecc71,
$danger: #e74c3c
);
3. @import(已废弃)
@import 是旧版 Sass 的模块加载方式,在现代项目中不再推荐使用。
主要问题:
- 全局污染:所有变量、mixin、函数直接注入全局作用域,容易命名冲突。
- 依赖关系不透明:难以追踪某个变量来自哪个
@import。 - 重复加载:多次
@import同一文件会被多次编译,增加 CSS 体积。 - 未来将被移除:Sass 官方计划逐步淘汰
@import。
// 旧写法(不推荐)
@import "variables";
@import "mixins";
.button {
background: $primary-color; // 不知道来自哪里
}
⚠️ 注意:如果你的项目仍在使用
@import,建议尽快迁移到@use。
4. 迁移建议:@import → @use
旧写法 (@import) | 新写法 (@use) |
|---|---|
@import 'variables';直接使用 $primary | @use 'variables';使用 variables.$primary |
多个 @import 顺序混乱 | 所有 @use 声明写在文件顶部,依赖清晰 |
| 全局混合导致命名冲突 | 命名空间隔离,显式调用 |
| 无法得知变量来源 | 一看 namespace.$var 就知道出处 |
| 同一文件被多次导入,生成重复 CSS | 模块仅加载一次,避免重复 |
迁移步骤:
- 将所有局部文件重命名为
_xxx.scss(如果尚未命名)。 - 在主入口文件中,将所有
@import替换为@use。 - 为每个
@use模块添加命名空间前缀(或自定义别名)。 - 检查变量、Mixin、函数调用,加上对应的命名空间。
- 移除
@import语句。
5. 模块化总结
| 特性 | @import(旧) | @use(新) |
|---|---|---|
| 作用域 | 全局污染 | 命名空间隔离 |
| 依赖清晰度 | 低(不透明) | 高(显式引用) |
| 重复加载 | 可能重复输出 | 模块只加载一次 |
| 配置变量 | 无原生支持 | 支持 with 配置 |
| 官方推荐 | ❌ 已废弃 | ✅ 强烈推荐 |
最佳实践:使用 _partials.scss 组织模块;在入口文件顶部用 @use 加载所有依赖;使用默认命名空间(文件名)或自定义简写别名;避免使用 @use ... as *,除非完全确信不会发生命名冲突;逐步将旧项目中的 @import 迁移到 @use。
二、内置模块与函数
Dart Sass 自 1.23.0 起提供以 sass: 为前缀的内置模块,通过 @use 加载,用于颜色、字符串、列表、Map、数学等运算。
1. 内置模块列表
| 模块 | 用途 |
|---|---|
sass:color | 颜色操作(生成、调整、混合颜色) |
sass:string | 字符串操作(合并、搜索、分割) |
sass:list | 列表操作(访问和修改列表中的值) |
sass:map | Map 映射操作(查找键值对等) |
sass:math | 数学运算(数值计算) |
sass:meta | 内省操作(检查 Sass 内部机制) |
sass:selector | 选择器操作 |
注意:传统全局函数(如
lighten())仍可全局调用,但推荐使用模块函数以获得更明确的命名空间和更好的可维护性。
2. 使用模块函数
需要通过 @use 导入模块,然后使用 模块名.函数名() 的方式调用。
@use "sass:math";
@use "sass:color";
$width: 960px;
$columns: 12;
.col {
// 使用 math.div() 代替已过时的 `/` 除法
width: math.div($width, $columns); // 80px
}
.btn {
background: #3498db;
&:hover {
// 使用 color.scale() 调整亮度
background: color.scale(#3498db, $lightness: 20%);
}
}
别名使用(简化命名):
@use "sass:math" as math;
@use "sass:color" as c;
.col {
width: math.div(960px, 12);
}
.btn:hover {
background: c.scale(#3498db, $lightness: 20%);
}
3. 常用内置函数速查
字符串函数(sass:string)
| 函数 | 说明 | 示例 |
|---|---|---|
string.unquote($string) | 删除字符串两端的引号 | unquote("Helvetica") → Helvetica |
string.quote($string) | 添加引号 | quote(Helvetica) → "Helvetica" |
string.length($string) | 返回字符串长度 | length("foo") → 3 |
string.slice($str, $start, $end) | 截取子字符串 | slice("hello", 2, 4) → "ell" |
数值函数(sass:math)
| 函数 | 说明 | 示例 |
|---|---|---|
math.percentage($number) | 将无单位数转为百分比 | percentage(0.5) → 50% |
math.round($number) | 四舍五入取整 | round(12.8) → 13 |
math.floor($number) | 向下取整 | floor(12.8) → 12 |
math.ceil($number) | 向上取整 | ceil(12.1) → 13 |
math.div($a, $b) | 除法运算(替代已过时的 /) | div(10px, 2) → 5px |
math.random() | 生成随机数(0~1) | random() → 0.456 |
颜色函数(sass:color)
| 函数 | 说明 | 示例 |
|---|---|---|
color.rgb($r, $g, $b) | 通过 RGB 创建颜色 | rgb(52, 152, 219) → #3498db |
color.mix($c1, $c2, $weight) | 混合两种颜色(weight 为第一色的占比) | mix(red, blue, 50%) → #800080 |
color.adjust($color, $lightness: 10%) | 调整颜色的 HSL 分量 | adjust(#3498db, $lightness: 10%) |
color.scale($color, $lightness: 20%) | 按比例缩放分量 | scale(#3498db, $lightness: 20%) |
color.complement($color) | 返回互补色 | complement(#3498db) → #db7f34 |
推荐:对于品牌色系微调,优先使用
color.adjust()或color.scale(),而非已过时的lighten()/darken(),因为后者在 HSL 空间中简单增减明暗可能产生意料之外的色调偏移。
列表函数(sass:list)
| 函数 | 说明 | 示例 |
|---|---|---|
list.length($list) | 返回列表长度 | length(10px 20px 30px) → 3 |
list.nth($list, $n) | 获取第 n 项(从 1 开始) | nth(10px 20px 30px, 2) → 20px |
list.append($list, $val, $separator) | 向列表末尾追加元素 | append((10px, 20px), 30px) → 10px, 20px, 30px |
list.join($list1, $list2) | 合并两个列表 | join((10px, 20px), (30px)) → 10px 20px 30px |
Map 函数(sass:map)
| 函数 | 说明 | 示例 |
|---|---|---|
map.get($map, $key) | 获取 Map 中指定键的值 | get((primary: blue), primary) → blue |
map.has-key($map, $key) | 判断 Map 是否包含某键 | has-key((a:1), b) → false |
map.merge($map1, $map2) | 合并两个 Map | merge((a:1), (b:2)) → (a:1, b:2) |
map.keys($map) | 返回 Map 的所有键组成的列表 | keys((a:1, b:2)) → a, b |
map.values($map) | 返回 Map 的所有值组成的列表 | values((a:1, b:2)) → 1, 2 |
内省函数(sass:meta)
| 函数 | 说明 | 示例 |
|---|---|---|
meta.type-of($value) | 返回值的类型 | type-of(10px) → number |
meta.unit($number) | 返回数值的单位 | unit(10px) → px |
meta.global-variable-exists($name) | 检查全局变量是否存在 | variable-exists("theme") |
4. 自定义函数
使用 @function 可以创建自己的 Sass 函数,用于封装复杂的计算逻辑。
@use "sass:math";
// 将 px 转换为 rem
@function px-to-rem($px, $base: 16px) {
@return math.div($px, $base) * 1rem;
}
h1 {
font-size: px-to-rem(32px); // 输出 2rem
}
// 带条件判断的复杂函数
@function opposite-color($color) {
@if lightness($color) > 50% {
@return black;
} @else {
@return white;
}
}
自定义函数注意事项:
- 函数名遵循 Sass 命名规则(可包含
-,不强制前缀)。 - 使用
@return返回值。 - 可以调用其他内置或自定义函数。
- 函数内可以使用控制指令(
@if、@each等,见下一节)。
5. 模块导入最佳实践
- 按需导入:只导入需要使用的模块,避免全局命名污染。
- 使用命名空间:通过
@use "sass:math" as math使代码可读性更强。 - 避免与旧全局函数混用:例如同时使用
math.div()和已过时的/除法可能导致混乱。 - 配置变量:一些模块允许使用
with配置,例如@use "sass:color" with ($hue: 120);(部分模块支持)。
三、控制指令
控制指令(@if、@for、@each、@while)用于在样式表中做条件判断与循环,生成重复或按数据驱动的规则。
1. @if / @else if / @else
根据条件输出不同的样式块,类似编程语言中的条件语句。
$theme: "dark";
.button {
@if $theme == "dark" {
background: black;
color: white;
} @else if $theme == "light" {
background: white;
color: black;
} @else {
background: gray;
}
}
编译结果(当 $theme: "dark" 时):
.button {
background: black;
color: white;
}
适用场景:主题切换、响应式断点选择、不同环境样式差异。
2. @for 循环
@for 循环有两种语法形式:
through:包含终止值(包头包尾)to:不包含终止值(包头不包尾)
// through:从 1 到 4(包含 4)
@for $i from 1 through 4 {
.col-#{$i} {
width: 100% / 4 * $i;
}
}
// to:从 1 到 4(不包含 4,即只到 3)
@for $i from 1 to 4 {
.item-#{$i} {
transform: translateX($i * 10px);
}
}
编译结果(through):
.col-1 {
width: 25%;
}
.col-2 {
width: 50%;
}
.col-3 {
width: 75%;
}
.col-4 {
width: 100%;
}
适用场景:生成栅格系统、等差数列样式、索引类名。
3. @each 循环
遍历列表(List)中的每个元素,或遍历 Map 中的每个键值对。
遍历列表
$colors: red blue green;
@each $color in $colors {
.bg-#{$color} {
background: $color;
}
}
编译结果:
.bg-red {
background: red;
}
.bg-blue {
background: blue;
}
.bg-green {
background: green;
}
遍历 Map
$themes: (
primary: #3498db,
success: #2ecc71,
danger: #e74c3c,
);
@each $name, $color in $themes {
.btn-#{$name} {
background: $color;
}
}
编译结果:
.btn-primary {
background: #3498db;
}
.btn-success {
background: #2ecc71;
}
.btn-danger {
background: #e74c3c;
}
适用场景:批量生成工具类、主题配置、图标映射等。
4. @while 循环
基于条件重复执行样式块,直到条件为 false。需手动控制循环变量,避免无限循环。
$i: 6;
@while $i > 0 {
.item-#{$i} {
width: $i * 10px;
}
$i: $i - 1;
}
编译结果:
.item-6 {
width: 60px;
}
.item-5 {
width: 50px;
}
.item-4 {
width: 40px;
}
.item-3 {
width: 30px;
}
.item-2 {
width: 20px;
}
.item-1 {
width: 10px;
}
适用场景:复杂条件循环(如某些特定步长或直到某个条件满足),但通常 @for 或 @each 更简洁直观。
总结
| 主题 | 常用语法 | 用途 |
|---|---|---|
| 模块化 | @use "x" as ns | 命名空间隔离、避免全局污染 |
| 内置模块 | math.div()、color.scale() | 数值/颜色/字符串/列表/Map 计算 |
| 自定义函数 | @function + @return | 封装复杂计算逻辑 |
@if | @if $a == 1 { ... } | 条件判断 |
@for | @for $i from 1 through 5 | 已知范围循环(含边界) |
@each | @each $item in $list | 遍历列表或 Map |
@while | @while $i > 0 { ... } | 不定条件循环(慎用) |
优先使用 @use 而非 @import;控制指令中优先 @each / @for,慎用复杂 @while。
参考文献
| 资料 | 说明 |
|---|---|
| Sass:@use | 模块加载 |
| Sass:@forward | 转发模块成员 |
| Sass:@import 弃用 | 迁移说明 |
| Sass:Built-in Modules | sass:math、color 等 |
| Sass:@function | 自定义函数 |
| Sass:Control Directives | @if、@for、@each |
相关文章
Sass 简介与安装
Sass(Syntactically Awesome Style Sheets)是 CSS 预处理器,在 CSS 语法上增加变量、嵌套、混合、模块、控制指令等;有 SCSS 与缩进语法两种源码语法,官方实现为 Dart Sass。
复用机制:Mixin 与继承
Mixin(@mixin/@include)支持参数与 @content,适合动态样式;@extend 与 %placeholder 通过合并选择器复用完全相同的静态样式,减少 CSS 体积。
语法基础:变量、嵌套与运算
Sass 语法基础:$ 变量与插值、选择器嵌套与 & 父选择器引用、数学/颜色/字符串运算、三种注释语法及与原生 CSS 嵌套的关系。
Less 基础与核心语法
Less 是 JavaScript 实现的 CSS 预处理器,在 CSS 语法上扩展变量(@)、嵌套、混合、运算与函数,编译为普通 CSS 后由浏览器加载;本文覆盖安装、CLI、构建工具集成与全部核心语法。
颜色与背景
color 设置前景(多为文字)色;background-* 设置元素背景(颜色、图像、渐变)。二者共同决定对比度与视觉层次,直接影响 无障碍 阅读体验。排版中的文本颜色用法见 排版与文本。
工程化集成与扩展
Less 可集成到 Vite、Webpack、Vue CLI 等工具链,实现编译、全局变量注入与路径处理。
Series
sass
2 / 4