目录
- [SCSS 简介与环境搭建](#SCSS 简介与环境搭建)
- 变量:样式数据的核心管理
- 嵌套:让代码结构跟随HTML
- 混合(Mixin):代码复用的利器
- 继承(@extend):样式共享的高效方式
- 运算与函数:动态生成样式
- 控制指令:@if、@for、@each、@while
- [模块化系统:@use 与 @forward(现代推荐)](#模块化系统:@use 与 @forward(现代推荐))
- 实战场景:响应式设计与全局配置
- 常见错误与避坑指南
- 总结与进阶建议
1. SCSS 简介与环境搭建
1.1 什么是 SCSS?
SCSS 是 CSS 的"增强版",写法上与 CSS 高度相似(保留 {} 和 ;),但多了很多能提升效率的功能。它是 Sass(世界上最成熟、最稳定的专业级 CSS 扩展语言)的一种语法格式,与之对应的还有缩进式的 Sass 语法,但 SCSS 因为完全兼容 CSS 语法(任何合法的 CSS 文件都能直接改成 .scss 文件使用),成为了绝对的主流。
核心概念:我们编写 .scss 文件,然后使用 Sass 预处理器将其编译为浏览器能够识别的 .css 文件,最终应用到项目中。
1.2 环境搭建
Step 1:安装 Sass 预处理器
安装前提,装好node 和 npm 环境
shell
node -v
npm -v
Step 2:全局安装
适用于命令行编译 SCSS 文件,可以通过命令行将 .scss 编译为 .css
shell
npm install -g sass
sass --version
Step 3:安装 Sass 预处理器
在项目中安装 sass 依赖:
bash
# 使用 npm
npm install sass --save-dev
# 使用 yarn
yarn add sass -D
# 使用 pnpm
pnpm add sass -D
Step 4:在 Vue/Vite 项目中启用 SCSS
在 vite.config.js 中进行基础配置:
javascript
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
// 基础配置(全局注入详见第9章)
}
}
}
})
Step 5:编译测试
创建一个 style.scss 文件:
scss
$primary-color: #42b983;
body {
background: $primary-color;
h1 {
color: white;
}
}
运行编译命令:
bash
npx sass style.scss style.css
生成 style.css:
css
body {
background: #42b983;
}
body h1 {
color: white;
}
2. 变量:样式数据的核心管理
2.1 变量的定义与使用
SCSS 中用 $ 符号开头定义变量,可以在整个样式表中引用。
scss
// 定义基础变量
$color-primary: #0081ff; // 主色
$color-secondary: #f5f5f5; // 辅助色
$font-size-base: 14px; // 基础字体大小
$border-radius: 4px; // 圆角大小
$spacing-unit: 8px; // 间距单位
$transition-duration: 0.25s; // 过渡时长
// 引用变量
.btn-primary {
background-color: $color-primary;
color: #fff;
font-size: $font-size-base;
border-radius: $border-radius;
padding: $spacing-unit * 2 $spacing-unit * 3; // 支持数学运算
transition: background-color $transition-duration ease;
}
.card {
background-color: #fff;
border: 1px solid $color-secondary;
border-radius: $border-radius;
}
2.2 变量的作用域
SCSS 变量支持块级作用域:在嵌套规则内部定义的变量是局部变量,外部无法访问。
scss
$global-color: red; // 全局变量
.container {
$local-color: blue; // 局部变量,仅在此规则内有效
color: $global-color; // ✅ 可以访问全局变量
background: $local-color; // ✅ 可以访问局部变量
}
.sidebar {
color: $global-color; // ✅ 可以访问全局变量
background: $local-color; // ❌ 错误!局部变量外部无法访问
}
强制转换为全局变量 :使用 !global 标志可以将局部变量提升为全局变量。
scss
.container {
$theme-color: green !global; // 强制设为全局变量
color: $theme-color;
}
.sidebar {
color: $theme-color; // ✅ 现在可以访问了
}
2.3 默认变量 !default
当变量可能被重复定义时,使用 !default 设置默认值:如果该变量之前已被赋值,则使用已有值;否则使用默认值。
scss
// _variables.scss
$primary-color: #0081ff !default;
$spacing: 16px !default;
// 主题覆盖文件
$primary-color: #ff5722; // 覆盖默认值
// 使用时会得到 #ff5722
.button {
background: $primary-color;
}
2.4 构建间距比例尺系统
利用变量和 map 构建完整的设计系统:
scss
$spacing-base: 4px;
$spacing-scale: (
'xs': $spacing-base, // 4px
'sm': $spacing-base * 2, // 8px
'md': $spacing-base * 4, // 16px
'lg': $spacing-base * 6, // 24px
'xl': $spacing-base * 8, // 32px
'2xl': $spacing-base * 12 // 48px
);
.card {
margin-bottom: map-get($spacing-scale, 'md'); // 16px
padding: map-get($spacing-scale, 'lg'); // 24px
}
.section {
padding: map-get($spacing-scale, 'xl'); // 32px
}
3. 嵌套:让代码结构跟随 HTML
3.1 选择器嵌套
原生 CSS 写嵌套结构时选择器会越来越长,SCSS 的嵌套语法允许直接跟着 HTML 层级书写。
scss
// SCSS 写法
.header {
width: 100%;
padding: 20px 0;
.nav {
display: flex;
justify-content: space-between;
.list {
display: flex;
gap: 20px;
.item {
color: #333;
cursor: pointer;
}
}
}
}
编译后的 CSS:
css
.header { width: 100%; padding: 20px 0; }
.header .nav { display: flex; justify-content: space-between; }
.header .nav .list { display: flex; gap: 20px; }
.header .nav .list .item { color: #333; cursor: pointer; }
3.2 父选择器引用 &
& 代表嵌套规则外层的父选择器,常用于伪类和修饰符。
scss
// 错误写法:没有使用 &
.main1 {
a {
:hover { color: red; } // 编译后 .main1 a :hover(中间有空格)
}
}
// 正确写法:使用 &
.main1 {
a {
&:hover { color: red; } // 编译后 .main1 a:hover
}
}
更丰富的 & 用法:
scss
.button {
background-color: blue;
&:hover {
background-color: darkblue; // .button:hover
}
&.active {
font-weight: bold; // .button.active
}
&-large {
padding: 16px 24px; // .button-large
}
&-small {
padding: 8px 12px; // .button-small
}
.icon {
margin-right: 8px; // .button .icon(不使用 &)
}
}
3.3 属性嵌套
对于相同前缀的属性(如 font-*、border-*),可以进行属性嵌套:
scss
// SCSS
.funky {
font: 20px/24px {
family: fantasy;
weight: bold;
}
}
.text {
font: {
size: 12px;
family: fantasy;
}
border: {
radius: 4px;
width: 1px;
style: solid;
color: #ccc;
}
}
编译后的 CSS:
css
.funky {
font: 20px/24px;
font-family: fantasy;
font-weight: bold;
}
.text {
font-size: 12px;
font-family: fantasy;
border-radius: 4px;
border-width: 1px;
border-style: solid;
border-color: #ccc;
}
3.4 群组嵌套
同时为多个选择器应用相同样式:
scss
#content {
h1, h2, h3, h4 {
border: solid;
margin: 0 0 10px 0;
}
}
编译结果:
css
#content h1, #content h2, #content h3, #content h4 {
border: solid;
margin: 0 0 10px 0;
}
最佳实践:嵌套深度不宜超过 3 层,否则编译后的 CSS 选择器过长,影响性能和可维护性。
4. 混合(Mixin):代码复用的利器
4.1 基础用法
混合指令(Mixin)用于定义可重复使用的样式,可以包含所有的 CSS 规则,甚至通过参数功能引入变量。
scss
// 定义无参数混合
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
@mixin large-text {
font: {
family: Arial;
size: 20px;
weight: bold;
}
color: #ff0000;
}
// 使用混合
.container {
@include flex-center;
}
.page-title {
@include large-text;
padding: 4px;
margin-top: 10px;
}
4.2 带参数的混合(核心功能)
scss
// 带参数的混合(推荐添加默认值)
@mixin link-colors($normal, $hover, $visited) {
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}
@mixin rounded-button($radius: 4px, $bg: $color-primary, $color: white) {
border-radius: $radius;
background-color: $bg;
color: $color;
padding: 8px 16px;
border: none;
cursor: pointer;
}
// 调用方式
.link {
@include link-colors(blue, red, purple);
}
.btn-primary {
@include rounded-button; // 使用默认值
}
.btn-large {
@include rounded-button(8px, #28a745, white); // 覆盖所有参数
}
.btn-small {
@include rounded-button($radius: 2px); // 只覆盖圆角
}
4.3 可变参数
当不确定参数数量时,使用 ... 接收可变参数:
scss
@mixin 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.05));
}
// 配合 transition 使用
@mixin transitions($properties...) {
transition: $properties;
}
.button {
@include transitions(background 0.3s ease, transform 0.2s ease);
}
4.4 内容块传递(@content)
使用 @content 可以让混合接收一段自定义样式,极大提升了灵活性:
scss
// 响应式断点混合
@mixin respond-to($breakpoint) {
@media (min-width: $breakpoint) {
@content;
}
}
// 使用示例
.sidebar {
width: 100%;
@include respond-to(768px) {
width: 300px;
}
@include respond-to(1200px) {
width: 400px;
}
}
// 更实用的断点混合
$breakpoints: (
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px
);
@mixin media-up($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}
}
.grid {
display: grid;
grid-template-columns: 1fr;
@include media-up('md') {
grid-template-columns: repeat(2, 1fr);
}
@include media-up('lg') {
grid-template-columns: repeat(3, 1fr);
}
}
5. 继承(@extend):样式共享的高效方式
5.1 基础继承
当一个样式与另一个样式几乎完全相同,只有少量差异时,使用 @extend 可以避免代码冗余。
scss
// 基础样式
.contanier1 {
font-size: 24px;
font-weight: bold;
color: green;
}
// 继承并覆盖/扩展
.contanier2 {
@extend .contanier1;
color: red; // 覆盖 color 属性
}
.contanier3 {
@extend .contanier1;
font-size: 30px; // 覆盖 font-size
}
编译后:
css
.contanier1, .contanier2, .contanier3 {
font-size: 24px;
font-weight: bold;
color: green;
}
.contanier2 {
color: red;
}
.contanier3 {
font-size: 30px;
}
5.2 占位符选择器(%)
占位符选择器以 % 开头,只有当被 @extend 继承时才会生成 CSS,否则完全不会出现在编译结果中。
scss
// 定义占位符
%message-shared {
border: 1px solid #ccc;
padding: 10px;
margin: 10px 0;
}
%message-highlight {
background-color: #f8f9fa;
}
// 使用继承
.message-success {
@extend %message-shared;
@extend %message-highlight;
color: green;
}
.message-error {
@extend %message-shared;
color: red;
}
5.3 Mixin vs Extend 选择指南
| 特性 | @mixin | @extend |
|---|---|---|
| 代码体积 | 每次调用都会复制代码 | 选择器合并,代码更精简 |
| 灵活性 | 支持参数,非常灵活 | 不支持参数 |
| 适用场景 | 需要参数化定制时 | 样式完全相同,仅语义不同时 |
| 性能考虑 | 调用多次会增加文件大小 | 文件更小,但可能影响选择器特异性 |
scss
// 推荐使用 mixin 的场景:需要参数
@mixin button-variant($color, $bg) {
color: $color;
background: $bg;
&:hover { background: darken($bg, 10%); }
}
// 推荐使用 extend 的场景:样式完全一致
%visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.skip-link { @extend %visually-hidden; }
.screen-reader-text { @extend %visually-hidden; }
6. 运算与函数:动态生成样式
6.1 数学运算
SCSS 支持标准的数学运算:+、-、*、/、%。
scss
$base-font: 16px;
$base-spacing: 8px;
.container {
font-size: $base-font; // 16px
padding: $base-spacing * 2; // 16px
margin: $base-spacing / 2; // 4px
// 混合单位运算(需要确保单位兼容)
width: 100% - 20px;
// 注意:/ 在 CSS 属性中原本就存在,SCSS 有特殊规则
font: $base-font / 2; // 使用括号避免歧义
font: ($base-font / 2); // 明确为除法
}
// 间距系统
$spacing-unit: 4px;
.section {
padding: $spacing-unit * 6; // 24px
margin-bottom: $spacing-unit * 4; // 16px
}
6.2 字符串运算
使用 + 进行字符串拼接:
scss
$base-url: "/assets/images/";
.icon {
background-image: url($base-url + "icon.png");
}
// 使用插值语法 #{} 在字符串中嵌入变量
$name: "button";
$state: "hover";
.#{$name} {
&-#{$state} {
color: red;
}
}
6.3 颜色函数
Sass 提供了丰富的颜色处理函数:
scss
$brand-primary: #3a86ff;
.button-primary {
background-color: $brand-primary;
&:hover {
background-color: darken($brand-primary, 10%); // 加深 10%
}
&:active {
background-color: lighten($brand-primary, 5%); // 提亮 5%
}
}
// 透明度处理
.overlay {
background-color: rgba($brand-primary, 0.5);
background-color: transparentize($brand-primary, 0.5); // 同上
}
// 颜色混合
.mix-color {
background-color: mix(red, blue, 50%); // 紫色(红蓝各半)
}
// 饱和度调整
.subtle-bg {
background-color: desaturate($brand-primary, 30%); // 降低饱和度
}
6.4 自定义函数(@function)
使用 @function 定义自己的函数,返回计算后的值:
scss
// 定义像素转 rem 函数
@function px-to-rem($px-value, $base-font: 16px) {
@return ($px-value / $base-font) * 1rem;
}
// 计算间距函数
@function spacing($multiplier) {
@return $spacing-base * $multiplier;
}
// 使用示例
$spacing-base: 4px;
.heading {
font-size: px-to-rem(32px); // 2rem
margin-bottom: spacing(6); // 24px
}
// 更复杂的函数:计算亮度
@function is-light($color) {
$red: red($color);
$green: green($color);
$blue: blue($color);
$brightness: (($red * 299) + ($green * 587) + ($blue * 114)) / 1000;
@return $brightness > 128;
}
// 根据背景色自动选择文字颜色
@function text-color($bg-color) {
@if is-light($bg-color) {
@return #333;
} @else {
@return #fff;
}
}
.button-auto {
background: $brand-primary;
color: text-color($brand-primary);
}
7. 控制指令:@if、@for、@each、@while
7.1 @if 条件判断
@if 指令允许根据条件进行分支。
scss
@mixin theme-aware($theme) {
@if $theme == 'light' {
background: white;
color: black;
} @else if $theme == 'dark' {
background: #1a1a1a;
color: white;
} @else {
background: gray;
color: white;
}
}
.container {
@include theme-aware('dark');
}
// 结合响应式使用
@mixin responsive-font($size) {
@if $size == 'small' {
font-size: 12px;
} @else if $size == 'medium' {
font-size: 16px;
} @else {
font-size: 20px;
}
}
7.2 @for 循环
@for 循环用于生成规律重复的样式。through 包含结束值,to 不包含结束值。
scss
// 使用 through(包含结束值 3)
@for $i from 1 through 3 {
.col-#{$i} {
width: percentage($i / 3);
}
}
// 生成 .col-1 { width: 33.33333%; }
// .col-2 { width: 66.66667%; }
// .col-3 { width: 100%; }
// 使用 to(不包含结束值 3)
@for $i from 1 to 3 {
.item-#{$i} {
font-size: #{$i}em;
}
}
// 生成 .item-1 { font-size: 1em; }
// .item-2 { font-size: 2em; }
// 实际应用:延迟动画
@for $i from 1 through 10 {
.animate-item:nth-child(#{$i}) {
animation-delay: #{$i * 0.1}s;
}
}
// 网格系统
@for $i from 1 through 12 {
.grid-#{$i} {
width: calc(100% / 12 * #{$i});
}
}
7.3 @each 循环
@each 用于遍历列表或 map 中的值。
scss
// 遍历列表
$colors: red, blue, green, yellow;
@each $color in $colors {
.bg-#{$color} {
background-color: $color;
}
}
// 遍历 map(推荐用于主题系统)
$theme-colors: (
'primary': #007bff,
'success': #28a745,
'warning': #ffc107,
'danger': #dc3545
);
@each $name, $color in $theme-colors {
.btn-#{$name} {
background-color: $color;
border-color: $color;
&:hover {
background-color: darken($color, 10%);
}
}
}
// 同时遍历多个值
$icons: ('home', 'home'), ('user', 'user'), ('settings', 'cog');
@each $name, $glyph in $icons {
.icon-#{$name}:before {
content: $glyph;
}
}
7.4 @while 循环
@while 循环在条件为真时持续执行(使用频率较低,一般场景下 @for 和 @each 已足够):
scss
$i: 6;
@while $i > 0 {
.item-#{$i} {
z-index: $i;
}
$i: $i - 1;
}
// 生成 .item-6 { z-index: 6; } 到 .item-1 { z-index: 1; }
8. 模块化系统:@use 与 @forward(现代推荐)
8.1 为什么弃用 @import?
SCSS 中的 @import 已被标记为废弃,将在未来版本中移除。原因如下:
- 全局引入所有变量、混合和函数,导致命名冲突
- 无法控制加载顺序
- 重复导入时无法去重
新的模块系统(@use 和 @forward)通过显式命名空间 和按需加载解决了这些问题。
8.2 @use:模块化导入(推荐)
@use 将另一个 SCSS 文件当作"模块"加载,默认通过命名空间访问变量、函数和混入。
scss
// _variables.scss
$primary: #0b81ff !default;
$secondary: #6c757d !default;
@mixin btn {
padding: 8px 12px;
border-radius: 8px;
}
// _typography.scss
$font-family-base: 'Inter', sans-serif;
@function px-to-rem($px) {
@return ($px / 16px) * 1rem;
}
// main.scss - 使用 @use
@use 'variables' as vars; // 起别名
@use 'typography' as type; // 起别名
.button {
color: vars.$primary; // 通过命名空间访问变量
font-family: type.$font-family-base;
@include vars.btn(); // 通过命名空间调用 mixin
}
.title {
font-size: type.px-to-rem(24px); // 通过命名空间调用函数
}
使用 as * 去除命名空间(谨慎使用) :
scss
// 去除命名空间(全局暴露,可能导致冲突)
@use 'variables' as *;
.button {
color: $primary; // 直接使用,不加前缀
@include btn();
}
使用 with 配置默认变量:
scss
// 配置模块的 !default 变量
@use 'variables' as vars with (
$primary: #0052d9, // 覆盖默认值
$secondary: #666666
);
8.3 @forward:聚合导出
@forward 用于把若干模块"转发"出去,创建一个统一的入口文件(类似 barrel 文件)。
scss
// styles/_index.scss ------ 统一出口
@forward 'variables';
@forward 'mixins' show flex-center, grid; // 只暴露指定的 mixin
@forward 'functions' hide px-to-rem; // 隐藏指定的函数
@forward 'colors' as color-*; // 导出时添加前缀
// main.scss ------ 使用统一出口
@use 'styles' as style;
.button {
color: style.$color-primary; // 通过统一入口访问
@include style.flex-center();
}
8.4 完整的模块化项目结构示例
styles/
├── _variables.scss # 变量定义
├── _mixins.scss # 混合定义
├── _functions.scss # 函数定义
├── _index.scss # 统一出口(聚合转发)
└── components/
├── _button.scss
├── _card.scss
└── _index.scss
_index.scss(统一出口) :
scss
@forward 'variables';
@forward 'mixins';
@forward 'functions';
components/_button.scss :
scss
@use '../index' as *; // 导入所有样式模块
.button {
background: $primary;
border-radius: $border-radius;
@include transition();
}
9. 实战场景:响应式设计与全局配置
9.1 响应式设计最佳实践
基础用法:媒体查询嵌套
Sass 允许将 @media 直接嵌套在选择器内部,编译时会自动提升到最外层。
scss
.sidebar {
width: 100%;
@media (min-width: 768px) {
width: 50%;
}
@media (min-width: 1200px) {
width: 320px;
}
}
使用变量管理断点
scss
// _breakpoints.scss
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;
$breakpoint-xxl: 1400px;
// 使用变量
.container {
width: 100%;
@media (min-width: $breakpoint-md) {
width: 750px;
}
@media (min-width: $breakpoint-lg) {
width: 970px;
}
@media (min-width: $breakpoint-xl) {
width: 1170px;
}
}
封装响应式混合(企业级方案)
scss
// _responsive.scss
$breakpoints: (
'xs': 0,
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px,
'xxl': 1400px
);
// 向上响应(min-width)
@mixin up($breakpoint) {
$value: map-get($breakpoints, $breakpoint);
@if $value != null {
@media (min-width: $value) {
@content;
}
}
}
// 向下响应(max-width)
@mixin down($breakpoint) {
$value: map-get($breakpoints, $breakpoint);
@if $value != null {
@media (max-width: $value - 1) {
@content;
}
}
}
// 区间响应
@mixin between($min, $max) {
$min-value: map-get($breakpoints, $min);
$max-value: map-get($breakpoints, $max);
@if $min-value != null and $max-value != null {
@media (min-width: $min-value) and (max-width: $max-value - 1) {
@content;
}
}
}
// 使用示例
.card {
padding: 16px;
@include up('md') {
padding: 24px;
}
@include up('lg') {
padding: 32px;
display: flex;
}
}
.hide-on-mobile {
@include down('md') {
display: none;
}
}
CSS 自定义属性实现响应式变量
Sass 变量是编译时确定的静态值,无法在媒体查询中动态变化。要实现响应式变量,需要使用 CSS 自定义属性(CSS 变量)。
scss
// 定义断点
$breakpoint-md: 768px;
$breakpoint-xl: 1200px;
// 响应式 mixin
@mixin larger-than($breakpoint) {
@media (min-width: $breakpoint) {
@content;
}
}
// 在根元素定义响应式 CSS 变量
:root {
--header-height: 60px;
--container-padding: 16px;
@include larger-than($breakpoint-md) {
--header-height: 80px;
--container-padding: 24px;
}
@include larger-than($breakpoint-xl) {
--header-height: 120px;
--container-padding: 32px;
}
}
// 使用 CSS 变量
.header {
height: var(--header-height);
}
.container {
padding: var(--container-padding);
}
// CSS 变量与 Sass 变量结合使用
$colors: (
'primary': #007bff,
'success': #28a745,
'danger': #dc3545
);
:root {
@each $name, $color in $colors {
--color-#{$name}: #{$color};
}
}
.button-primary {
background-color: var(--color-primary);
}
9.2 Vite 全局注入 SCSS
在 Vite 项目中,通过配置 css.preprocessorOptions.scss.additionalData 可以将 SCSS 变量和混合全局注入。
javascript
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `
@use "@/styles/variables.scss" as vars;
@use "@/styles/mixins.scss" as *;
`
}
}
}
})
注意事项 :additionalData 中使用 @use 时,需要在组件中通过命名空间访问:
scss
// 在 Vue 组件中
<style lang="scss">
.button {
color: vars.$primary; // 需要命名空间前缀
@include flex-center(); // as * 时可以直接调用
}
</style>
最佳实践:创建聚合入口文件
scss
// src/styles/index.scss
@forward 'variables';
@forward 'mixins';
@forward 'functions';
javascript
// vite.config.js
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/index" as *;`
}
}
}
})
9.3 完整项目示例:主题切换系统
scss
// _themes.scss
$themes: (
light: (
bg-primary: #ffffff,
bg-secondary: #f5f5f5,
text-primary: #333333,
text-secondary: #666666,
border: #e0e0e0
),
dark: (
bg-primary: #1a1a1a,
bg-secondary: #2d2d2d,
text-primary: #ffffff,
text-secondary: #b3b3b3,
border: #404040
)
);
@mixin themed() {
@each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
@each $key, $value in $map {
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
@content;
$theme-map: null !global;
}
}
}
@function t($key) {
@return map-get($theme-map, $key);
}
// 使用示例
.card {
@include themed() {
background: t(bg-primary);
color: t(text-primary);
border: 1px solid t(border);
}
}
10. 常见错误与避坑指南
10.1 变量作用域陷阱
scss
// ❌ 错误:局部变量在外部无法访问
.nav {
$nav-color: blue;
color: $nav-color; // ✅
}
.footer {
color: $nav-color; // ❌ Error: Undefined variable
}
// ✅ 正确:需要时使用 !global 或将变量定义在顶层
$global-nav-color: blue;
10.2 父选择器误用
scss
// ❌ 错误:缺少 & 导致生成错误的选择器
.button {
:hover { color: red; } // 编译为 .button :hover(中间有空格)
}
// ✅ 正确:使用 & 引用父选择器
.button {
&:hover { color: red; } // 编译为 .button:hover
}
10.3 @import vs @use 选择
scss
// ⚠️ 旧方式(已弃用,会产生警告)
@import 'variables';
@import 'mixins';
// ✅ 新方式(推荐)
@use 'variables' as vars;
@use 'mixins';
.button {
color: vars.$primary;
@include mixins.flex-center();
}
10.4 过度嵌套
scss
// ❌ 不推荐:嵌套过深(4层以上)
.header {
.nav {
.menu {
.item {
.link {
color: blue; // 选择器过于具体,难以覆盖
}
}
}
}
}
// ✅ 推荐:保持嵌套不超过 3 层
.header {
.nav-menu {
.menu-item {
.link { color: blue; }
}
}
}
10.5 媒体查询变量误用
scss
// ❌ 错误:在媒体查询中修改 Sass 变量不会影响外部
$padding: 16px;
.container {
padding: $padding;
@media (min-width: 768px) {
$padding: 32px; // 这不会改变外部的 $padding
padding: $padding; // 在媒体查询内有效,但编译后是独立值
}
}
// ✅ 正确:直接覆盖样式,或使用 CSS 自定义属性
.container {
padding: 16px;
@media (min-width: 768px) {
padding: 32px;
}
}
10.6 变量命名规范建议
scss
// ❌ 差:基于具体值命名
$blue: #0081ff;
$red: #ff5722;
// ✅ 好:基于用途命名
$color-primary: #0081ff;
$color-secondary: #ff5722;
$color-accent: #00c853;