什么是 Sass
Sass(Syntactically Awesome Stylesheets)是一种 CSS 预处理器,它在 CSS 的基础上增加了变量、嵌套、混合宏、继承、函数等编程语言特性,最终编译为标准 CSS 文件。
为什么需要 CSS 预处理器
CSS 本身不是编程语言——它没有变量、没有函数、没有逻辑控制。当项目规模变大时,会产生大量重复代码(如重复的颜色值、尺寸、媒体查询等),维护和修改变得极其困难。预处理器通过编程抽象解决了这些问题。
Sass 与其他预处理器的区别
| 特性 | Sass (Dart Sass) | Less | Stylus |
|---|
| 运行环境 | Dart (原生编译最快) | Node.js | Node.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
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
| export default { css: { preprocessorOptions: { scss: { additionalData: `@use "@/styles/variables" as *;` } } } }
|
全局注入(避免每次手动 @use)
通过构建工具的 additionalData 配置,可以将公共变量、mixin 注入每个 SCSS 文件:
1 2 3 4 5 6 7
| { 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; color: $local; }
.footer { color: $global; color: $local; }
|
!global 覆盖全局变量
1 2 3 4 5 6 7 8 9 10
| $color: blue;
.card { $color: red !global; color: $color; }
.footer { color: $color; }
|
注意:频繁使用 !global 容易导致样式难以调试,建议尽量用 @use 模块化方案替代。
!default 主题默认值
!default 允许变量在未定义时才使用默认值,已定义时则沿用原值。这是组件库和主题系统的核心机制:
1 2 3 4
| $primary-color: #409eff !default; $font-size-base: 14px !default; $border-radius: 4px !default;
|
1 2 3 4 5 6
| $primary-color: #ff6600; @use 'variables';
|
数据类型
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; $half-gap: $gap / 2;
|
注意 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;
.card { border: 1px solid $border-color; }
@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);
$second: nth($colors, 2); $length: length($colors);
$colors: append($colors, yellow);
$index: index($colors, blue);
|
⚠ 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);
@each $name, $width in $breakpoints { @media (min-width: $width) { .container-#{$name} { max-width: $width; } } }
@if map-has-key($breakpoints, 'xl') { }
$extended: map-merge($breakpoints, (xxl: 1400px));
$keys: map-keys($breakpoints); $values: map-values($breakpoints);
|
综合示例:设计主题变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| $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; } }
|