一、Sass 简介:CSS 预处理器的行业标准
Sass(Syntactically Awesome Style Sheets)作为最成熟的 CSS 预处理器,已成为现代前端开发的必备工具。它通过引入变量、嵌套、混合宏等特性,解决了原生 CSS 的代码冗余、维护困难等痛点,被 Twitter、GitHub、Bootstrap 等大型项目广泛采用。本文将从基础语法到高级实战,全方位讲解 Sass 系统,帮助你构建可维护、可扩展的样式架构。
1.1 为什么选择 Sass?
原生 CSS 开发中存在三大核心痛点:变量缺失导致颜色 / 尺寸等常量重复定义、选择器冗长难以表达层级关系、代码复用困难造成大量重复样式。Sass 通过以下特性彻底解决这些问题:
特性 | 功能描述 | 解决的 CSS 痛点 |
---|---|---|
变量系统 | 使用$ 定义可复用的值(颜色、字体、尺寸等) |
常量值硬编码,修改需全局替换 |
嵌套规则 | 选择器可嵌套表示 HTML 层级关系 | 重复书写父选择器,代码结构不清晰 |
混合宏(Mixin) | 封装可复用的样式块,支持参数传递 | 相同样式块重复书写,难以统一维护 |
继承(@extend) | 让一个选择器继承另一个选择器的样式 | 多个选择器共享基础样式,产生冗余代码 |
模块化 | 使用@use 和@forward 拆分样式文件,实现真正的模块化 |
@import 导致全局作用域污染、变量冲突 |
控制指令 | 支持@if 条件判断、@for 循环等逻辑控制 |
无法动态生成样式,如响应式断点、主题切换 |
1.2 Sass 与其他预处理器对比
目前主流的 CSS 预处理器有 Sass、Less、Stylus,三者功能对比如下:
特性 | Sass | Less | Stylus |
---|---|---|---|
语法规范 | 两种语法:SCSS(CSS 兼容)和缩进语法 | 仅 CSS 兼容语法 | 支持无括号 / 分号的极简语法 |
变量定义 | $variable: value |
@variable: value |
variable = value |
模块化 | @use /@forward (真正模块化) |
@import (全局作用域) |
@import (全局作用域) |
控制指令 | 完整支持@if /@for /@each |
有限支持(需通过混合宏模拟) | 支持类 JavaScript 语法的控制流 |
社区生态 | 最成熟,Bootstrap/Foundation 等框架采用 | 生态较小,主要用于 React 项目 | 生态最小,多用于 Node.js 项目 |
性能 | Dart Sass 编译速度快,支持增量编译 | 基于 JavaScript,大型项目编译较慢 | 基于 Node.js,性能中等 |
结论:Sass 凭借更完善的特性、更成熟的生态和更快的编译速度,成为大多数企业项目的首选。
二、环境搭建:从安装到编译全流程
2.1 安装 Sass(三种方式)
2.1.1 npm/yarn 安装(推荐)
# npm
npm install -g sass
# yarn
yarn global add sass
2.1.2 Ruby 环境安装(传统方式)
# Windows:下载RubyInstaller(https://rubyinstaller.org/)
# macOS:brew install ruby
# Linux:sudo apt-get install ruby-full
# 安装Sass
gem install sass
2.1.3 独立安装包
从Sass 官网下载对应系统的独立安装包,解压后将可执行文件路径添加到环境变量。
2.2 验证安装
sass --version
# 输出类似:1.77.6
2.3 基础编译命令
单文件编译
# 源文件:src/scss/style.scss → 输出文件:dist/css/style.css
sass src/scss/style.scss dist/css/style.css
监听文件变化(开发必备)
# --watch:文件变化时自动编译
sass --watch src/scss:dist/css
生产环境编译(压缩输出)
# --style=compressed:压缩CSS
# --source-map:生成源映射文件(调试用)
sass src/scss/style.scss dist/css/style.min.css --style=compressed --source-map
2.4 编辑器配置(VSCode)
- 安装插件:Live Sass Compiler
- 配置编译选项(
.vscode/settings.json
):
{
"liveSassCompile.settings.formats": [
{
"format": "expanded", // 开发环境:展开格式
"extensionName": ".css",
"savePath": "/dist/css"
},
{
"format": "compressed", // 生产环境:压缩格式
"extensionName": ".min.css",
"savePath": "/dist/css"
}
],
"liveSassCompile.settings.sourceMap": true, // 生成源映射
"liveSassCompile.settings.autoprefix": ["> 1%", "last 2 versions"] // 自动添加浏览器前缀
}
三、核心语法:从基础到进阶
3.1 变量系统:样式常量的统一管理
3.1.1 基本使用
// 定义变量
$primary-color: #3498db; // 主色调
$font-size-base: 16px; // 基础字号
$spacing-unit: 8px; // 间距单位
// 使用变量
body {
color: $primary-color;
font-size: $font-size-base;
padding: $spacing-unit * 2; // 支持数学运算
}
3.1.2 变量作用域
$color: blue; // 全局变量
.container {
$color: red; // 局部变量(仅在.container内生效)
color: $color; // 输出red
}
.text {
color: $color; // 输出blue(全局变量)
}
3.1.3 默认变量(!default)
用于第三方库或主题系统,允许用户覆盖默认值:
// 库文件:_variables.scss
$border-radius: 4px !default; // 默认值
// 用户文件:custom.scss
$border-radius: 8px; // 覆盖默认值
@use 'variables'; // 引入库文件
.box {
border-radius: variables.$border-radius; // 输出8px
}
3.2 嵌套规则:HTML 结构的直观映射
3.2.1 选择器嵌套
// SCSS
nav {
ul {
list-style: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 $spacing-unit;
}
a {
text-decoration: none;
color: $primary-color;
&:hover { // &表示父选择器(nav a)
color: darken($primary-color, 10%); // 颜色加深10%
}
}
}
// 编译后CSS
nav ul {
list-style: none;
padding: 0;
}
nav li {
display: inline-block;
margin: 0 8px;
}
nav a {
text-decoration: none;
color: #3498db;
}
nav a:hover {
color: #2980b9;
}
3.2.2 属性嵌套
对font-
、margin-
等属性前缀进行嵌套:
.box {
font: { // font属性嵌套
family: 'Helvetica Neue', sans-serif;
size: 16px;
weight: bold;
}
margin: { // margin属性嵌套
top: 10px;
left: 20px;
}
}
// 编译后CSS
.box {
font-family: 'Helvetica Neue', sans-serif;
font-size: 16px;
font-weight: bold;
margin-top: 10px;
margin-left: 20px;
}
3.3 混合宏(Mixin):带参数的样式模板
3.3.1 基础混合宏
// 定义混合宏
@mixin clearfix {
&::after {
content: "";
display: table;
clear: both;
}
}
// 使用混合宏
.container {
@include clearfix; // 引入清除浮动样式
}
3.3.2 带参数混合宏
// 定义带参数混合宏(默认值:$radius: 4px)
@mixin button($bg-color, $text-color: white, $radius: 4px) {
background: $bg-color;
color: $text-color;
padding: 10px 20px;
border-radius: $radius;
border: none;
cursor: pointer;
&:hover {
background: darken($bg-color, 10%); // 内置颜色函数:加深颜色
}
}
// 使用混合宏
.btn-primary {
@include button(#3498db); // 使用默认文本色和圆角
}
.btn-danger {
@include button(#e74c3c, white, 6px); // 传递所有参数
}
3.3.3 可变参数(处理不定数量参数)
@mixin box-shadow($shadows...) { // ...表示可变参数
-webkit-box-shadow: $shadows;
-moz-box-shadow: $shadows;
box-shadow: $shadows;
}
.card {
@include box-shadow(0 2px 4px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.2));
}
3.4 继承(@extend):样式复用的最佳实践
3.4.1 基础继承
// 基础样式
.base-button {
padding: 10px 20px;
border-radius: 4px;
font-size: 14px;
}
// 继承基础样式并扩展
.primary-button {
@extend .base-button; // 继承.base-button的所有样式
background: $primary-color;
color: white;
}
.secondary-button {
@extend .base-button;
background: $secondary-color;
color: white;
}
3.4.2 占位符选择器(%)
避免未使用的基础样式编译到 CSS 中:
// 占位符选择器(不会编译到CSS)
%base-button {
padding: 10px 20px;
border-radius: 4px;
}
.primary-button {
@extend %base-button; // 仅继承时才会编译
background: $primary-color;
}
3.5 控制指令:动态生成样式
3.5.1 @if 条件判断
$theme: dark;
body {
@if $theme == light {
background: white;
color: black;
} @else if $theme == dark {
background: #333;
color: white;
} @else {
background: #f5f5f5;
}
}
3.5.2 @for 循环(生成栅格系统)
// 生成1-12列的栅格
@for $i from 1 through 12 {
.col-#{$i} { // #{}:变量插值
width: percentage($i / 12); // 内置函数:转换为百分比
}
}
// 编译后CSS
.col-1 { width: 8.33333%; }
.col-2 { width: 16.66667%; }
/* ... 直到.col-12 */
3.5.3 @each 循环(遍历列表 / 映射)
// 列表遍历
$sizes: small 12px, medium 16px, large 20px;
@each $name, $size in $sizes {
.text-#{$name} {
font-size: $size;
}
}
// 映射遍历(键值对)
$colors: (
primary: #3498db,
secondary: #2ecc71
);
@each $name, $color in $colors {
.text-#{$name} {
color: $color;
}
}
四、模块化开发:@use 与 @forward 彻底替代 @import
Sass 1.80.0 正式弃用@import
,推出@use
和@forward
实现真正的模块化。
4.1 @import 的致命缺陷
- 全局作用域污染:变量 / 混合宏全局可见,易冲突
- 重复编译:多次导入同一文件导致冗余 CSS
- 依赖混乱:无法明确变量 / 混合宏的来源
4.2 @use:引入模块并创建命名空间
4.2.1 基础用法
// _variables.scss(下划线开头:局部文件,不会单独编译)
$primary-color: #3498db;
// style.scss
@use 'variables'; // 引入variables模块(省略下划线和扩展名)
body {
color: variables.$primary-color; // 通过命名空间访问变量
}
4.2.2 自定义命名空间
@use 'variables' as vars; // 自定义命名空间为vars
body {
color: vars.$primary-color;
}
4.2.3 导入所有成员(不推荐,可能冲突)
@use 'variables' as *; // 所有成员导入当前作用域
body {
color: $primary-color; // 直接访问变量(无命名空间)
}
4.3 @forward:转发模块成员
用于创建公共 API,整合多个模块:
// _components.scss
@forward 'button'; // 转发button模块
@forward 'card'; // 转发card模块
@forward 'form'; // 转发form模块
// 外部使用时只需引入components
@use 'components'; // 可访问button、card、form的所有成员
选择性转发
// 隐藏内部成员,只暴露公共API
@forward 'utils' hide internal-mixin, $internal-var; // 隐藏指定成员
@forward 'theme' show $primary, $secondary; // 只暴露指定成员
4.4 配置模块(with 关键字)
许用户覆盖模块的默认变量:
// _theme.scss
$primary: blue !default; // 默认值
$secondary: green !default;
// style.scss
@use 'theme' with (
$primary: #3498db, // 覆盖默认值
$secondary: #2ecc71
);
body {
color: theme.$primary; // 使用配置后的变量
}
五、内置模块:Sass 的原生工具库
Sass 提供sass:color
、sass:math
等内置模块,扩展样式处理能力。
5.1 sass:color(颜色处理)
@use 'sass:color';
$base: #3498db;
.element {
// 调整颜色:增加10%亮度,降低20%饱和度
background: color.adjust($base, $lightness: 10%, $saturation: -20%);
// 混合颜色:30%红色 + 70%蓝色
border-color: color.mix(red, blue, 30%);
// 获取颜色通道:红色值
$red: color.red($base); // 52(#3498db的R通道值)
}
5.2 sass:math(数学运算)
@use 'sass:math';
.box {
// 精确除法(避免CSS语法冲突)
width: math.div(100, 3); // 33.3333333333px
// 四舍五入
height: math.round(15.6px); // 16px
// 常量π
$circle-circumference: 2 * math.$pi * 50px; // 圆周长计算
}
5.3 sass:map(映射操作)
@use 'sass:map';
$theme: (
colors: (
primary: #3498db,
secondary: #2ecc71
),
breakpoints: (
sm: 576px,
md: 768px
)
);
// 获取嵌套映射值
$primary-color: map.get($theme, colors, primary); // #3498db
// 检查键是否存在
@if map.has-key($theme, breakpoints) {
// 遍历映射
@each $name, $size in map.get($theme, breakpoints) {
@media (min-width: $size) {
.container { max-width: $size; }
}
}
}
六、实战案例:从响应式到主题切换
6.1 响应式布局系统(移动优先)
6.1.1 定义断点变量
// _breakpoints.scss
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
// 断点混合宏
@mixin breakpoint-up($name) {
$min-width: map.get($breakpoints, $name);
@media (min-width: $min-width) {
@content; // 插入外部样式
}
}
6.1.2 响应式组件
@use 'breakpoints';
.card {
width: 100%; // 移动优先:默认100%宽度
@include breakpoints.breakpoint-up(md) {
width: 50%; // 中等屏幕:50%宽度
}
@include breakpoints.breakpoint-up(lg) {
width: 33.333%; // 大屏幕:33.333%宽度
}
}
6.2 主题切换系统
6.2.1 主题变量定义
// _themes.scss
$themes: (
light: (
text: #333,
background: #fff,
primary: #3498db
),
dark: (
text: #fff,
background: #333,
primary: #4fa3e0
)
);
6.2.2 主题混合宏
@use 'sass:map';
// 生成主题样式
@mixin theme-styles($theme-name) {
$theme: map.get($themes, $theme-name);
[data-theme="#{$theme-name}"] { // CSS属性选择器
--text-color: map.get($theme, text);
--bg-color: map.get($theme, background);
--primary-color: map.get($theme, primary);
}
}
// 生成所有主题
@each $name, $theme in $themes {
@include theme-styles($name);
}
6.2.3 HTML 中切换主题
<!-- 默认主题 -->
<body>...</body>
<!-- 切换到暗色主题 -->
<body data-theme="dark">...</body>
6.2.4 使用 CSS 变量
body {
color: var(--text-color);
background: var(--bg-color);
}
.btn-primary {
background: var(--primary-color);
}
6.3 组件库开发(BEM 命名规范)
6.3.1 BEM 命名混合宏
// _bem.scss
@mixin b($block) {
.#{$block} {
@content;
}
}
@mixin e($element) {
$block: &; // &:父选择器
.#{$block}__#{$element} {
@content;
}
}
@mixin m($modifier) {
$block: &;
.#{$block}--#{$modifier} {
@content;
}
}
6.3.2 开发 Button 组件
@use 'bem';
@include bem.b(button) { // 块(block)
padding: 10px 20px;
border: none;
@include bem.e(icon) { // 元素(element)
margin-right: 8px;
}
@include bem.m(primary) { // 修饰符(modifier)
background: #3498db;
}
@include bem.m(large) { // 修饰符
padding: 15px 30px;
}
}
// 编译后CSS
.button { padding: 10px 20px; border: none; }
.button__icon { margin-right: 8px; }
.button--primary { background: #3498db; }
.button--large { padding: 15px 30px; }
七、工程化集成:Webpack 与自动化工作流
7.1 Webpack 配置 Sass
7.1.1 安装依赖
npm install sass sass-loader css-loader style-loader postcss-loader autoprefixer --save-dev
7.1.2 Webpack 配置(webpack.config.js)
module.exports = {
module: {
rules: [
{
test: /\.scss$/i,
use: [
"style-loader", // 将CSS注入DOM
"css-loader", // 解析CSS imports
{
loader: "postcss-loader", // 自动添加浏览器前缀
options: {
postcssOptions: {
plugins: [
require("autoprefixer")({
overrideBrowserslist: ["last 2 versions"]
})
]
}
}
},
"sass-loader" // 编译Sass为CSS
]
}
]
}
};
7.2 Gulp 自动化工作流
7.2.1 安装依赖
npm install gulp gulp-sass sass gulp-autoprefixer gulp-sourcemaps gulp-clean-css --save-dev
7.2.2 Gulp 配置(gulpfile.js)
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const autoprefixer = require('gulp-autoprefixer');
const sourcemaps = require('gulp-sourcemaps');
const cleanCSS = require('gulp-clean-css');
// 开发环境:编译Sass并生成源映射
gulp.task('sass:dev', () => {
return gulp.src('src/scss/**/*.scss')
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(autoprefixer())
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist/css'));
});
// 生产环境:压缩CSS
gulp.task('sass:prod', () => {
return gulp.src('src/scss/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(cleanCSS())
.pipe(gulp.dest('dist/css'));
});
// 监听文件变化
gulp.task('watch', () => {
gulp.watch('src/scss/**/*.scss', gulp.series('sass:dev'));
});
// 默认任务:开发环境+监听
gulp.task('default', gulp.series('sass:dev', 'watch'));
八、性能优化:从编译到加载全链路优化
8.1 编译优化
- 使用 Dart Sass:比 Node Sass 快 2-10 倍
- 增量编译:
sass --watch
只编译变化的文件 - 输出风格:开发环境用
expanded
(易读),生产环境用compressed
(最小体积)
8.2 代码优化
- 减少嵌套深度:嵌套不超过 3 层,避免生成复杂选择器
- 合理使用继承:优先用
@extend
复用样式,减少 CSS 体积 - 按需加载:通过媒体查询拆分非关键 CSS,如打印样式、大屏样式
8.3 加载优化
- Tree-shaking:使用 PurgeCSS 移除未使用样式
javascript
// postcss.config.js module.exports = { plugins: [ require('@fullhuman/postcss-purgecss')({ content: ['./src/**/*.html', './src/**/*.js'] // 扫描HTML/JS中的类名 }) ] };
- CSS-in-JS:结合 Webpack 的
mini-css-extract-plugin
拆分 CSS 文件 - HTTP/2:多 CSS 文件并行加载(配合域名分片)
九、常见问题与解决方案
9.1 编译错误
- 变量未定义:检查
@use
路径是否正确,变量是否通过命名空间访问 - 混合宏参数不匹配:确保
@include
时传递的参数数量与@mixin
定义一致 - 嵌套过深:减少嵌套层级,避免超过 5 层
9.2 与 CSS Modules 共存
Sass 变量与 CSS Modules 作用域隔离:
// style.module.scss
$primary: red; // Sass变量(模块内可见)
:export { // 导出为JS变量
primary: $primary;
}
// JS中导入
import styles from './style.module.scss';
console.log(styles.primary); // 'red'
9.3 浏览器兼容
- CSS 变量:IE 不支持,需用 PostCSS 插件
postcss-css-variables
转译 - 新特性:使用
autoprefixer
自动添加浏览器前缀
十、总结与进阶学习
Sass 通过变量、嵌套、模块化等特性,彻底改变了 CSS 的开发模式。掌握 Sass 不仅能提升样式开发效率,更能构建可维护、可扩展的前端样式架构。
进阶学习资源
- 官方文档:Sass Documentation
- 实战项目:Bootstrap 源码(Sass 最佳实践)
- 工具链:SassDoc(文档生成)、Stylelint(代码检查)