SCSS从0到1精通教程

目录

  1. [SCSS 简介与环境搭建](#SCSS 简介与环境搭建)
  2. 变量:样式数据的核心管理
  3. 嵌套:让代码结构跟随HTML
  4. 混合(Mixin):代码复用的利器
  5. 继承(@extend):样式共享的高效方式
  6. 运算与函数:动态生成样式
  7. 控制指令:@if、@for、@each、@while
  8. [模块化系统:@use 与 @forward(现代推荐)](#模块化系统:@use 与 @forward(现代推荐))
  9. 实战场景:响应式设计与全局配置
  10. 常见错误与避坑指南
  11. 总结与进阶建议

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;
相关推荐
ZC跨境爬虫2 小时前
海南大学交友平台登录页开发实战day6(覆写接口+Flask 本地链接正常访问)
前端·后端·python·flask·html
Highcharts.js2 小时前
抉择之巅:从2029年回望2026年——企业可视化“战略分水岭”?
前端·javascript·信息可视化·编辑器·echarts·highcharts
沙振宇2 小时前
【Web】使用Vue3+PlayCanvas开发3D游戏(十)让人物动起来
前端·游戏·3d·人物·
军军君012 小时前
数字孪生监控大屏实战模板:空气污染监控
前端·javascript·vue.js·typescript·前端框架·echarts·数字孪生
m0_694845573 小时前
opendataloader-pdf部署教程:构建PDF数据处理系统
服务器·前端·前端框架·pdf·开源
小李子呢02113 小时前
前端八股浏览器网络(1)---响应头
前端
倚栏听风雨3 小时前
详细讲解下 for...of vs for await...of 区别
前端
REDcker3 小时前
Safari 26.4 新增 WebTransport:对 iOS WebView 的影响与落地建议
前端·ios·safari
练习前端两年半3 小时前
Vue3 KeepAlive 深度揭秘:组件缓存的魔法是如何实现的?
前端·vue.js·面试