FORMA

模块与控制流:@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 ❌(开头没有下划线,会被当作独立根文件编译)。

推荐目录结构

text
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 开始稳定支持。

基本加载与命名空间

scss
// _variables.scss
$primary-color: #3498db;
$border-radius: 4px;

// main.scss
@use "variables"; // 引入时省略下划线和扩展名

.button {
  background: variables.$primary-color; // 必须通过命名空间访问
  border-radius: variables.$border-radius;
}
  • 命名空间默认为文件名(不含 _ 和扩展名),如 variables
  • 有效避免了不同文件间的变量/函数命名冲突。

自定义命名空间

scss
@use "variables" as vars;

.button {
  background: vars.$primary-color;
}

一次性导入所有成员到当前作用域(不推荐)

scss
@use "variables" as *; // 所有成员直接暴露,无需前缀
.button {
  background: $primary-color; // 命名冲突风险高
}

不推荐,因为会失去模块化的优势,依然可能导致全局污染。

同时加载多个模块

scss
@use "variables";
@use "mixins";
@use "components/button" as btn;

.element {
  @include btn.style; // 调用 mixin
  color: variables.$text-color;
}

配置模块变量(需使用 !default

scss
// _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
scss
// 旧写法(不推荐)
@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模块仅加载一次,避免重复

迁移步骤

  1. 将所有局部文件重命名为 _xxx.scss(如果尚未命名)。
  2. 在主入口文件中,将所有 @import 替换为 @use
  3. 为每个 @use 模块添加命名空间前缀(或自定义别名)。
  4. 检查变量、Mixin、函数调用,加上对应的命名空间。
  5. 移除 @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:mapMap 映射操作(查找键值对等)
sass:math数学运算(数值计算)
sass:meta内省操作(检查 Sass 内部机制)
sass:selector选择器操作

注意:传统全局函数(如 lighten())仍可全局调用,但推荐使用模块函数以获得更明确的命名空间和更好的可维护性。

2. 使用模块函数

需要通过 @use 导入模块,然后使用 模块名.函数名() 的方式调用。

scss
@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%);
  }
}

别名使用(简化命名):

scss
@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)合并两个 Mapmerge((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 函数,用于封装复杂的计算逻辑。

scss
@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

根据条件输出不同的样式块,类似编程语言中的条件语句。

scss
$theme: "dark";

.button {
  @if $theme == "dark" {
    background: black;
    color: white;
  } @else if $theme == "light" {
    background: white;
    color: black;
  } @else {
    background: gray;
  }
}

编译结果(当 $theme: "dark" 时):

css
.button {
  background: black;
  color: white;
}

适用场景:主题切换、响应式断点选择、不同环境样式差异。

2. @for 循环

@for 循环有两种语法形式:

  • through:包含终止值(包头包尾)
  • to:不包含终止值(包头不包尾)
scss
// 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)

css
.col-1 {
  width: 25%;
}
.col-2 {
  width: 50%;
}
.col-3 {
  width: 75%;
}
.col-4 {
  width: 100%;
}

适用场景:生成栅格系统、等差数列样式、索引类名。

3. @each 循环

遍历列表(List)中的每个元素,或遍历 Map 中的每个键值对。

遍历列表

scss
$colors: red blue green;

@each $color in $colors {
  .bg-#{$color} {
    background: $color;
  }
}

编译结果:

css
.bg-red {
  background: red;
}
.bg-blue {
  background: blue;
}
.bg-green {
  background: green;
}

遍历 Map

scss
$themes: (
  primary: #3498db,
  success: #2ecc71,
  danger: #e74c3c,
);

@each $name, $color in $themes {
  .btn-#{$name} {
    background: $color;
  }
}

编译结果:

css
.btn-primary {
  background: #3498db;
}
.btn-success {
  background: #2ecc71;
}
.btn-danger {
  background: #e74c3c;
}

适用场景:批量生成工具类、主题配置、图标映射等。

4. @while 循环

基于条件重复执行样式块,直到条件为 false。需手动控制循环变量,避免无限循环。

scss
$i: 6;
@while $i > 0 {
  .item-#{$i} {
    width: $i * 10px;
  }
  $i: $i - 1;
}

编译结果:

css
.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 Modulessass:math、color 等
Sass:@function自定义函数
Sass:Control Directives@if、@for、@each