Sass/SCSS 语法速查表 - Sass 常用语法与 Mixin 大全
当样式表开始出现大段重复声明,Sass 的价值就不在语法糖,而在于把「值」和「片段」抽出来:颜色与间距这类标量进变量与 Map,配值逻辑走函数,重复的声明序列收进 Mixin,需要复用的选择器块用 % 占位符配合 @extend。工具链上选 SCSS 而非缩进语法,因为它是 CSS 超集,老文件能直接改;模块加载则应当用 @use 替代 @import,重复加载同一文件会明显拖慢编译。表里按变量运算、Map 与颜色函数、嵌套、Mixin 与继承、函数循环、条件控制、模块引入分组。判断标准也很明确:如果改动只应该影响一处,就抽成变量或 Mixin;如果允许任意选择器复用,用 @extend;如果需要携带参数与逻辑,用 @mixin —— 三者混用前先想清楚扩展点会在编译产物里留下多少重复选择器。
典型使用场景
面向维护一套中型设计系统或组件库样式的前端工程师:统一色板与间距刻度、消除重复的 flex 居中代码、让断点集中管理、避免选择器嵌套膨胀到不可维护。Sass 把这些变成语言层面的能力。本表覆盖变量与运算(含 !default 与插值)、Map 与颜色函数、嵌套与 @at-root、mixin 与 @extend 的取舍、函数与循环、条件与控制指令、以及模块化引入(@use / @forward 与已废弃的 @import)。需要建立的两个判断是:@extend 会让选择器组合爆炸并影响其它规则,通常优先用 mixin;@import 已被官方标记为废弃并在新版 Dart Sass 里逐步移除,新代码应该一律用 @use。
变量与运算 5
$primary: #6366f1color: $primary$primary: #6366f1 !defaultwidth: 100px + 50pxcontent: "icon-#{$name}"Map 与颜色函数 8
$sizes: (small: 12px, large: 24px)width: map-get($sizes, small)map-keys($sizes)map-merge($sizes, (medium: 16px))color: darken(#6366f1, 10%)color: lighten(#6366f1, 10%)color: mix(red, blue)color: rgba(#6366f1, 0.5)嵌套 4
nav { ul { margin: 0 } }a { &:hover { color: red } }.btn { &-primary { color: blue } }.box { @at-root .inner { } }Mixin 与继承 8
@mixin flex-center { display: flex; justify-content: center }@include flex-center@mixin responsive($breakpoint) { @media ... }@mixin button { @content }.btn { @include button { color: red } }%message-box { border: 1px solid }@extend %message-box.error { @extend %message-box }函数与循环 4
@function px-to-rem($px) { @return $px / 16 * 1rem }@for $i from 1 through 3 { .col-#{$i} { } }@each $color in red, blue, green { .#{$color} { } }@while $i > 0 { .item-#{$i} { } $i: $i - 1 }条件与控制 5
@if $type == dark { background: black }@else if $type == light { background: white }@else { background: gray }@warn "变量未定义"@error "参数错误"引入与模块化 6
@import "variables"@use "variables" as *@use "variables" as vars@use "config" with ($primary: red)@forward "variables"@import url("https://fonts.googleapis.com")参数矩阵
| 参数 | 作用 | 示例 |
|---|---|---|
$primary: #6366f1 | 声明 Sass 变量,编译后消失,不会产生运行时 CSS 输出 | $primary: #6366f1 |
color: $primary | 在属性值里使用变量,是色板统一的基本用法 | color: $primary |
$primary: #6366f1 !default | 仅在变量尚未被赋值时才生效,是库文件暴露可覆盖配置的惯用写法 | $primary: #6366f1 !default |
width: 100px + 50px | 同类单位可直接相加;不同单位相加会报错,需用 calc 或先转换 | width: 100px + 50px |
#{$name} | 插值:把变量嵌进选择器、属性名或字符串,是生成循环类名的前提 | content: "icon-#{$name}" |
$sizes: (small: 12px, large: 24px) | Map 存键值对,比一串平铺变量更利于按语义取值 | $sizes: (small: 12px, large: 24px) |
map-get($sizes, small) | 按 key 取值;key 不存在时返回 null 而不是报错 | width: map-get($sizes, small) |
map-keys($sizes) | 取出所有 key,可配合 @each 遍历整张 Map | map-keys($sizes) |
map-merge($sizes, (medium: 16px)) | 合并 Map,用于在局部覆盖默认刻度而不改动原 Map | map-merge($sizes, (medium: 16px)) |
darken / lighten | 按固定比例变暗/变亮;因为是线性调整而非感知均匀,视觉上常不如预期 | color: darken(#6366f1, 10%) |
mix(red, blue) | 按比例混合两个颜色,生成中间色 | color: mix(red, blue) |
rgba(#6366f1, 0.5) | 由色值加透明度生成 rgba,比手写两个色值更不易写错 | color: rgba(#6366f1, 0.5) |
nav { ul { margin: 0 } } | 嵌套编译成后代选择器,让作用域化写法成立;过深嵌套会让选择器膨胀 | nav { ul { margin: 0 } } |
&:hover | & 代表父选择器,常用于伪类与伪元素 | a { &:hover { color: red } } |
&-primary | & 后直接拼接可生成同级新类名(如 .btn-primary),不必重复前缀 | .btn { &-primary { color: blue } } |
@at-root | 跳出当前嵌套输出选择器,是引入第三方选择器或 BEM 结构时的必需品 | .box { @at-root .inner { } } |
@mixin flex-center { ... } | 定义可复用的样式片段;不产生选择器,只在被 @include 时展开 | @mixin flex-center { display: flex; justify-content: center } |
@include flex-center | 展开一个 mixin;带参数时在括号里传参 | @include flex-center |
@mixin responsive($breakpoint) { @media ... } | 把 @media 封进 mixin,断点集中管理,避免各处硬编码尺寸 | @mixin responsive($breakpoint) { @media ... } |
@mixin button { @content } | 用 @content 预留内容块,实现带自定义内容的复用片段 | @mixin button { @content } |
@include button { color: red } | 给带 @content 的 mixin 传入内容块 | .btn { @include button { color: red } } |
%message-box | 占位选择器,只定义样式不生成输出,供 @extend 复用 | %message-box { border: 1px solid } |
@extend %message-box | 把占位样式并入当前选择器;会改变选择器组合,影响范围超出预期时优先改用 mixin | .error { @extend %message-box } |
@function px-to-rem($px) { @return ... } | 定义 Sass 函数,在样式值里调用,编译后函数消失 | @function px-to-rem($px) { @return $px / 16 * 1rem } |
@for $i from 1 through 3 | 按序号循环,配合插值生成 .col-1 .col-2 .col-3 | @for $i from 1 through 3 { .col-#{$i} { } } |
@each $color in red, blue, green | 遍历列表或 Map 的键,批量生成同构规则 | @each $color in red, blue, green { .#{$color} { } } |
@while $i > 0 | 条件循环,写法更灵活也更容易写出死循环,工程里优先用 @for | @while $i > 0 { .item-#{$i} { } $i: $i - 1 } |
@if / @else if / @else | 在编译期做分支,用于按配置切换主题或尺寸 | @if $type == dark { background: black } |
@warn | 打印警告但继续编译,适合提示未定义变量这类非致命问题 | @warn "变量未定义" |
@error | 直接中止编译并给出消息,用于参数非法这类必须失败的情况 | @error "参数错误" |
@use "variables" | 加载模块;命名空间隔离,变量需带前缀访问,是官方推荐写法 | @use "variables" |
@use "variables" as * | 把模块成员并入当前作用域,接近旧 @import 的体验,但仅限此文件 | @use "variables" as * |
@use "variables" as vars | 给模块起命名空间别名,访问时写 vars.$primary | @use "variables" as vars |
@use "config" with ($primary: red) | 带 !default 的变量可在加载时覆盖,是配置注入的正规做法 | @use "config" with ($primary: red) |
@forward "variables" | 把本文件的成员转发给上层,让一个入口文件统一收口多个模块 | @forward "variables" |
@import "variables" | 旧引入方式,全局作用域且重复引入会重复输出,已被官方废弃 | @import "variables" |
@import url("https://fonts.googleapis.com") | 保留 CSS 原样输出,用于无法用 Sass 表达的第三方样式 | @import url("https://fonts.googleapis.com") |
易错点与避坑指南
现象编译警告提示 @import 已废弃,未来版本会移除。
原因@import 仍是旧项目的主力引入方式;Dart Sass 已把多个 @import 合并为 @use。
处置新代码一律用 @use / @forward;存量项目按模块逐个迁移,一个文件一个模块地改。
现象同一份样式被打包了两遍,CSS 体积翻倍。
原因多个文件都 @import 了同一个模块,而 @import 没有命名空间隔离也没有去重效果。
处置改用 @use,模块只会被加载一次;多个入口文件用 @forward 收口。
现象@extend 之后某些不相干的规则也被改写了。
原因@extend 会把选择器组合进所有匹配处,嵌套深、占位选择器多时组合数量迅速膨胀。
处置把共享样式写成带参数的 mixin 用 @include;只在选择器结构稳定、影响面清晰时用 @extend。
现象darken 之后颜色看起来几乎没变,或者变了但达不到想要的对比度。
原因darken/lighten 是按固定比例做线性调整,与视觉感知不一致;需求往往是「与白底达到某对比度」。
处置改用 modern-color 的 color.adjust 并指定明度目标,或直接用设计系统里验证过的色阶变量。
现象变量明明在某处赋值了,其它地方却读到空值。
原因@use 的模块是命名空间隔离的,必须写 names.$var,直接引用 $var 会报未定义。
处置统一用 @use 里的命名空间访问;确需全局便利时用 as * 但只在该文件内使用。
现象覆盖默认值没生效,样式仍是库里的原值。
原因!default 只在该变量「尚未被赋值」时生效;如果当前文件先赋值了变量,再 @use 就覆盖不了。
处置改用 @use "config" with ($primary: red) 在加载时注入,这是官方推荐的配置方式。
现象编译极慢,输出 CSS 里出现大量重复的选择器。
原因深层嵌套叠加 @extend,组合爆炸;另一常见原因是嵌套里写了选择器而不是声明块。
处置限制嵌套层数(通常不超过 3 层),共享样式改用 mixin,并用 sourcemap 定位重复来源。
现象编译报 "Undefined variable",但变量确实在某个文件里定义了。
原因文件加载顺序问题:变量在使用之后才被定义,Sass 按出现顺序求值。
处置把变量集中到单一入口文件并最先 @use;配合 @forward 让依赖顺序显式化。
排障路径
1定位已废弃指令的位置
grep -rn "@import" src --include="*.scss"逐个确认是 Sass 模块引入还是保留的 CSS 原样输出。
2统计混用 @use 与 @import 的文件
grep -rln "@use" src --include="*.scss"与上一条结果对照,找出尚未迁移的模块。
3排查 @extend 的实际影响范围
grep -rn "@extend" src --include="*.scss"每处 @extend 都要能说清它会命中哪些选择器。
4找出所有 !default 变量
grep -rn "!default" src --include="*.scss"确认哪些变量允许被外部覆盖。
5检查深嵌套导致的重复规则
grep -rn "^ *\(16,\)" src --include="*.scss"过深缩进往往对应过深的嵌套与选择器组合。
6确认硬编码色值是否已收敛到变量
grep -rn "#[0-9a-fA-F]\{3,6\}" src --include="*.scss"散落的字面色值是主题切换失效的常见原因。
命令示例
带参数的 mixin 收敛媒体查询
@mixin responsive($breakpoint) {
@media (max-width: $breakpoint) {
@content;
}
}
.card {
@include responsive(768px) {
display: block;
}
}断点散落在十几个文件里,改一次要全局搜索替换。收进一个带参 mixin 后,断点只在定义处出现一次,其余地方全部走 @include。@content 让调用方决定块内容,这样 mixin 管「什么时候生效」,调用点管「生效时改什么」。
map 存设计 token,@use 带命名空间导入
$sizes: (small: 12px, large: 24px)
@use "variables" as vars
.btn {
font-size: vars.$sizes__small;
padding: map-get(vars.$sizes, small);
}旧式 @import 把变量铺进全局作用域,同名 $primary 在不同文件里互相覆盖,编译通过但结果不是你想要的;@use 的命名空间把每个模块的变量隔离开,重名也只是 vars.$primary 和 theme.$primary 之别。而且同一个模块 @use 多次只会执行一次,@import 多次则重复输出全部内容。
输出
.btn {
font-size: 12px;
padding: 12px;
}用 !default 做可被外部覆盖的默认值
$primary: #6366f1 !default
@use "config" with ($primary: red)
.btn { color: $primary }!default 的语义是「已定义则不覆盖」:只有当 $primary 尚未被赋值时才取右边的值。@use ... with (...) 会在模块被载入前注入配置值,专为这种场景设计。少了 !default,调用方就无法定制主题色,只能改源文件。
输出
.btn {
color: red;
}@at-root 跳出嵌套作用域
.theme-dark {
color: white;
@at-root .input {
border-color: #444;
}
}嵌套在 .theme-dark 里的 .input 默认会编译成 .theme-dark .input(后代选择器),如果本意是「无论在哪个主题下都用这个全局样式」,就必须用 @at-root 把它提到顶层。这是最容易写错、也最难一眼看出的 Sass 语义之一。
输出
.theme-dark {
color: white;
}
.input {
border-color: #444;
}颜色工具函数统一调节主题色
$primary: #6366f1
.btn {
background: $primary;
color: white;
}
.btn:hover { background: darken($primary, 10%) }
.btn:disabled { background: lighten($primary, 10%) }把品牌色收敛成一个 $primary 后,hover、disabled、浅色背景都从它推导出来(darken / lighten / mix / rgba),换肤只改一行。注意 rgba($color, $alpha) 改的是透明度不是颜色本身;只想要半透明底色时用 rgba($primary, 0.5),会连同颜色的不透明度一起算。
输出
.btn:hover {
background: #5046e0;
}常见坑与注意事项
- 继续用 @import 而不是 @use 是最常见的隐患:@import 全局共享作用域,同名 $primary、@mixin 会在后一个文件里静默覆盖前一个,编译不报错但产物颜色全变。迁移期先查 @import 数量,优先把共享变量文件改成 @use "variables" as * 或 as vars。
- 用 @import 加载含 @use 的文件会报错,混用两套模块系统时构建失败的位置常常指向完全不相关的文件。看到 "This file is already loaded" 或 "Cannot find stylesheet to import" 之类的报错,先把该文件的 @import 换成 @use。
- 颜色运算函数 darken / lighten / mix 在实现上做的是数值插值,不是感知亮度调节。#6366f1 darken 10% 视觉上不一定「更暗」,在黄色系上 lighten 常常看不出变化。设计稿对比明显时,改成手写具体色值或用 color.adjust 配合 $lightness。
- 尺寸除法:Sass 里 10px / 2 的结果仍然是 10px(除法只在特定场景下生效),想算比例必须写成 math.div(10px, 2)。在旧版本里 / 被当作分隔符,写错时编译能过但输出的是原值,样式直接不生效且无提示。
- @error 会立刻终止编译,@warn 只打印提示继续跑。把 @error 写在公共 mixin 的参数校验里时,某个页面传入非法参数会让整个站点构建失败——把这类校验留给确实无法降级的场景,其余用 @warn 保证可构建。
- @extend 占位符选择器会合并选择器、扩大匹配范围,用在通用类(如 %message-box 被 .error 和 .warning 同时继承)时可能把不该命中的元素也拉进来,且每加一个 @extend 全样式表都要重编译,大工程里明显拖慢构建。优先用 mixin 组合。
提示
- SCSS 语法(.scss)兼容 CSS,Sass 语法(.sass)用缩进,推荐用 SCSS。
- @use 是 Sass 推荐的新模块化方式,比 @import 更清晰。
- Mixin 适合复用代码块,% 占位符适合复用样式(不生成多余代码)。
- 用 @use "sass:math" 后调用 math.div($a, $b),除法不再是斜杠写法,避免与旧语法混淆。
- map.get($config, "primary", $fallback) 提供默认值键,比先 map.has-key 再取值更简洁。
- 编译时加 --no-source-map 减小产物体积;调试定位问题需要映射时再单独生成一次。
常见问题
@import 和 @use 都能引文件,新项目该用哪个?
新项目用 @use,它是推荐的模块化方式,加载后命名空间清晰。@use "variables" as vars 之后变量要写 vars.$primary;@use "variables" as * 则省略前缀,等价于旧行为。想覆盖模块里的默认值(那些声明了 !default 的变量),用 @use "config" with ($primary: red)。老代码里的 @import "variables" 属于旧语法,全局作用域污染容易出命名冲突,新代码别再用。它还有另一个正当用途:@import url("https://fonts.googleapis.com") 引入外部 CSS。
Mixin、@extend 和 % 占位符该怎么分工?
Mixin 复用「声明块」,用来输出属性和值:@mixin flex-center { display: flex; justify-content: center } 定义后 @include flex-center 调用;需要调用方塞内容进去就加 @content,再由 .btn { @include button { color: red } } 传入。% 占位符复用「样式组」,%message-box { border: 1px solid } 定义后 @extend %message-box 继承,它自身不会生成任何 CSS 选择器。简单说:一段属性用 Mixin,一组选择器共用的样式用 % 加 @extend。
要生成一组有规律的类名,循环和插值怎么配合?
循环负责产出次数,插值 #{} 负责把变量拼进选择器。@for $i from 1 through 3 { .col-#{$i} { } } 生成 .col-1 到 .col-3;@each $color in red, blue, green { .#{$color} { } } 按值遍历;@while $i > 0 { .item-#{$i} { } $i: $i - 1 } 在条件不满足时停止。插值同样用于属性值:content: "icon-#{$name}" 会生成 icon-xxx 字面量,而不是把 #{$name} 当普通字符输出。
响应式断点不想到处复制粘贴,怎么抽象?
把断点做成带参数的 Mixin:@mixin responsive($breakpoint) { @media ... } 定义一次,调用时把具体值传进去,媒体查询条件写在 Mixin 体内。Map 可以集中管理断点:$sizes: (small: 12px, large: 24px) 配合 map-get($sizes, small) 取值,需要合并新档位用 map-merge($sizes, (medium: 16px))。嵌套跳出用 @at-root:.box { @at-root .inner { } } 让 .inner 输出到顶层而不是编译成 .box .inner。
SCSS 里的数值运算和颜色函数,实际能省多少事?
数值可以直接参与运算:width: 100px + 50px 编译成 150px,自定义函数也常用,如 @function px-to-rem($px) { @return $px / 16 * 1rem }。颜色侧有 darken(#6366f1, 10%)、lighten(#6366f1, 10%)、mix(red, blue) 做混色,rgba(#6366f1, 0.5) 直接给主色加透明度,不必手写 rgba 值。调试时 @warn "变量未定义" 只提示继续编译,@error "参数错误" 则直接中止编译,适合给 Mixin 的必填参数做校验。
由 巧匠 维护
公开更新于 2026年7月21日,内容持续校对官方文档。