Sass 入门与变量

什么是 Sass

Sass(Syntactically Awesome Stylesheets)是一种 CSS 预处理器,它在 CSS 的基础上增加了变量、嵌套、混合宏、继承、函数等编程语言特性,最终编译为标准 CSS 文件。

为什么需要 CSS 预处理器

CSS 本身不是编程语言——它没有变量、没有函数、没有逻辑控制。当项目规模变大时,会产生大量重复代码(如重复的颜色值、尺寸、媒体查询等),维护和修改变得极其困难。预处理器通过编程抽象解决了这些问题。

Sass 与其他预处理器的区别

特性Sass (Dart Sass)LessStylus
运行环境Dart (原生编译最快)Node.jsNode.js
语法SCSS / 缩进语法类 CSS极简缩进
成熟度业界主流较老,生态减少小众
功能性全面(循环、Map、模块系统)基础灵活但规范弱

Sass 是目前社区最成熟、生态最完善的 CSS 预处理器,也是 Vue/React 等框架的默认推荐。

Sass 的编译

1. 安装

Sass 官方推荐使用 Dart Sass(已取代旧的 Node Sass):

1
npm install sass --save-dev

历史背景:早期使用 node-sass(基于 LibSass),但 LibSass 已于 2020 年弃用,Dart Sass 是官方当前维护的唯一实现。

2. 命令行编译

1
2
3
4
5
6
7
8
9
10
11
# 单文件编译
npx sass input.scss output.css

# 监听文件变化自动编译
npx sass --watch input.scss output.css

# 监听整个目录
npx sass --watch src/scss:dist/css

# 生成 source maps(便于调试)
npx sass --watch src/scss:dist/css --style expanded --source-map

3. 在构建工具中集成

Webpack (sass-loader)

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
}

Vite(内置支持)

1
2
3
4
5
6
7
8
9
10
// vite.config.js
export default {
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/variables" as *;`
}
}
}
}

全局注入(避免每次手动 @use)

通过构建工具的 additionalData 配置,可以将公共变量、mixin 注入每个 SCSS 文件:

1
2
3
4
5
6
7
// Webpack
{
loader: 'sass-loader',
options: {
additionalData: `@use "@/styles/variables" as *; @use "@/styles/mixins" as *;`
}
}

SCSS 语法 vs 缩进语法

Sass 提供两种语法:

SCSS(推荐)

1
2
3
4
5
6
$primary: #409eff;

.card {
border: 1px solid $primary;
&:hover { border-color: darken($primary, 10%); }
}

完全兼容 CSS,所有合法的 CSS 也是合法的 SCSS,迁移成本为零。

缩进语法(.sass)

1
2
3
4
5
6
$primary: #409eff

.card
border: 1px solid $primary
&:hover
border-color: darken($primary, 10%)

省略花括号和分号,依赖缩进。简洁但学习曲线较高,新项目不建议使用。

日常开发均使用 .scss 后缀

变量

声明与使用

1
2
3
4
5
6
7
8
9
10
11
// 普通变量
$font-size-base: 16px;
$primary-color: #409eff;
$border-radius: 4px;

// 在样式中使用
.button {
font-size: $font-size-base;
color: $primary-color;
border-radius: $border-radius;
}

变量的命名

变量名中的连字符和下划线是等价的,$primary-color$primary_color 指向同一个变量。但建议统一风格,团队约定。

变量作用域

1
2
3
4
5
6
7
8
9
10
11
$global: #333; // 全局变量

.card {
$local: #666; // 局部变量,仅限 .card 内部使用
color: $local;
}

.footer {
color: $global; // ✅
color: $local; // ❌ 编译错误:Undefined variable
}

!global 覆盖全局变量

1
2
3
4
5
6
7
8
9
10
$color: blue;

.card {
$color: red !global; // 覆盖全局变量
color: $color;
}

.footer {
color: $color; // red(已被 !global 修改)
}

注意:频繁使用 !global 容易导致样式难以调试,建议尽量用 @use 模块化方案替代。

!default 主题默认值

!default 允许变量在未定义时才使用默认值,已定义时则沿用原值。这是组件库和主题系统的核心机制:

1
2
3
4
// _variables.scss(主题配置文件)
$primary-color: #409eff !default;
$font-size-base: 14px !default;
$border-radius: 4px !default;
1
2
3
4
5
6
// 用户覆盖:在引入 variables 之前定义即可
$primary-color: #ff6600;
@use 'variables';

// $primary-color → #ff6600(用户覆盖)
// $font-size-base → 14px(使用默认值)

数据类型

SassScript 支持以下数据类型:

1. 数字(Numbers)

可以带或不带单位:

1
2
3
4
5
6
7
8
$width: 100px;      // 带单位
$ratio: 1.5; // 不带单位
$line-height: 1.6; // 无单位数字

// 运算
$gap: 16px;
$double-gap: $gap * 2; // 32px
$half-gap: $gap / 2; // 8px(Dart Sass 中 / 仅用于除法)

注意 Dart Sass 对 / 的处理:在 calc() 中使用 / 是 CSS 除法,在 $a / $b 中建议用 math.div($a, $b)

2. 字符串(Strings)

分带引号和不带引号:

1
2
3
4
5
6
$font-stack: 'Helvetica', Arial, sans-serif;  // 带引号
$direction: ltr; // 不带引号

// 插值在字符串中使用
$prefix: 'icon';
.#{$prefix}-home { ... }

3. 颜色(Colors)

1
2
3
4
5
6
7
8
$primary: #409eff;
$success: rgba(103, 194, 58, 0.8);
$warning: hsl(36, 80%, 60%);

// 颜色运算
$darker: darken($primary, 10%);
$lighter: lighten($primary, 20%);
$transparent: rgba($primary, 0.5);

4. 布尔值(Booleans)

1
2
3
4
5
6
$is-dark: true;
$has-shadow: false;

@if $is-dark {
background: #222;
}

5. Null

1
2
3
4
5
6
7
8
9
10
$border-color: null;   // 不会输出任何 CSS

.card {
border: 1px solid $border-color; // 编译后只保留 border: 1px solid
}

// 常用于条件判断
@if $border-color != null {
border-color: $border-color;
}

6. 列表(Lists)

列表是 Sass 中最常用的复合数据类型,用空格或逗号分隔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 空格分隔
$padding: 10px 20px 10px 20px;

// 逗号分隔
$fonts: 'Helvetica', Arial, sans-serif;

// 一维列表
$colors: red green blue;

// 二维列表(嵌套)
$theme-colors: (primary blue) (success green) (danger red);

// 访问列表元素(索引从 1 开始!)
$second: nth($colors, 2); // green
$length: length($colors); // 3

// 向列表添加元素
$colors: append($colors, yellow); // red green blue yellow

// 查找元素索引
$index: index($colors, blue); // 3

⚠ Sass 列表索引从 1 开始,不是从 0 开始。这是 Sass 新手最常见的错误。

7. Maps(键值对)

Map 是键值对集合,类似于 JavaScript 的对象或 Map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
);

// 取值
$md-width: map-get($breakpoints, md); // 768px

// 遍历
@each $name, $width in $breakpoints {
@media (min-width: $width) {
.container-#{$name} { max-width: $width; }
}
}

// 判断 key 是否存在
@if map-has-key($breakpoints, 'xl') {
// ...
}

// 合并 Map
$extended: map-merge($breakpoints, (xxl: 1400px));

// 获取所有 key 或 value
$keys: map-keys($breakpoints); // sm, md, lg, xl
$values: map-values($breakpoints); // 576px, 768px, 992px, 1200px

综合示例:设计主题变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// _theme.scss
$colors: (
primary: #409eff,
success: #67c23a,
warning: #e6a23c,
danger: #f56c6c,
info: #909399,
) !default;

$font-sizes: (
xs: 12px,
sm: 13px,
md: 14px,
lg: 16px,
xl: 18px,
) !default;

$spacings: (
xs: 4px,
sm: 8px,
md: 12px,
lg: 16px,
xl: 20px,
) !default;

配合 @each 可以批量生成工具类:

1
2
3
@each $name, $size in $font-sizes {
.fs-#{$name} { font-size: $size; }
}