CSS 工程化进阶:Sass/SCSS 在 Vue 项目中的实战用法

在学完原生 CSS 之后,很多人都会接触到一个新名词:Sass / SCSS

这时常见的疑惑通常有三个:

  1. 现代浏览器都已经这么强了,为什么项目里还会用 Sass?
  2. Vue 3 + Vite 项目里,scss 到底该怎么接?
  3. 既然有 CSS 变量了,Sass 还有必要学吗?

这些问题都很正常。

因为 Sass 不是“替代 CSS 的另一门语言”,而是一个建立在 CSS 之上的样式工程化工具。它解决的不是“浏览器能不能识别某个属性”,而是:

  • 样式是否更容易复用
  • 变量、模块和设计规范是否更容易维护
  • 大型项目里的样式组织是否足够清晰

如果你把 Vue 组件理解为“视图模块”,那么 Sass 更像是帮助你管理“样式模块”的工具。

本文就来系统讲清楚 Sass/SCSS 在 Vue 项目中的常用知识点、关键知识点,以及它和原生 CSS 变量之间到底该怎么取舍。

1. 先搞清楚:Sass、SCSS 到底是什么关系

很多初学者会把这两个词当成两个完全不同的技术,其实不是。

Sass 是这个预处理器的总称,而它有两种语法风格:

  • Sass 缩进语法:不写大括号和分号
  • SCSS 语法:写法更接近普通 CSS,最常用

例如,下面是 Sass 缩进语法:

$primary-color: #409eff

.button
  color: $primary-color

下面是 SCSS 语法:

$primary-color: #409eff;

.button {
  color: $primary-color;
}

在现代 Vue 项目里,大家几乎都使用 SCSS,因为它更接近原生 CSS,迁移成本更低。

所以你在项目里看到:

<style lang="scss">
</style>

本质上就是在使用 Sass 的 SCSS 语法。


2. 为什么现代项目还会用 Sass

很多人会问:

现在原生 CSS 已经有变量、有嵌套趋势、有现代布局了,为什么还要 Sass?

答案是:

因为 Sass 解决的不只是“语法糖”,更是样式组织和工程化复用的问题。

2.1 Sass 最常见的价值

在真实项目里,Sass 最常见的用途主要有这些:

  • 变量统一管理:品牌色、间距、圆角、阴影、字体大小集中维护
  • 样式嵌套:让组件内部层级结构更清晰
  • 混入复用:把重复样式逻辑抽成 mixin
  • 模块拆分:把全局变量、主题、函数、工具样式拆到独立文件
  • 样式计算:比如对间距、颜色、尺寸进行组合和推导

2.2 最适合用 Sass 的场景

Sass 特别适合以下项目:

  • 有统一设计规范的后台管理系统
  • 有公共组件库的项目
  • 页面较多、样式重复较多的中大型项目
  • 需要做主题变量管理的业务系统

2.3 不要神化 Sass

Sass 很有用,但不是“用了就高级”。

它并不能自动解决:

  • CSS 命名混乱
  • 组件职责不清
  • 样式污染
  • 页面结构混乱

如果项目本身结构很乱,Sass 只会让“更复杂的混乱”变得更难维护。

所以真正专业的思路应该是:

先有清晰的样式组织规则,再用 Sass 来提高复用和维护效率。


3. 在 Vite 项目中接入 SCSS

3.1 安装依赖

如果你使用的是 Vue 3 + Vite 项目,接入 SCSS 非常简单。先安装 sass

npm install -D sass

这里注意:

  • 现在通常安装的是 sass
  • 不再推荐使用旧的 node-sass

装好之后,Vite 就能自动识别 .scss 文件和 <style lang="scss"> 语法。

3.2 在 Vue 单文件组件里使用

最常见的写法就是:

<template>
  <button class="login-btn">登录</button>
</template>

<style scoped lang="scss">
.login-btn {
  padding: 12px 20px;
  border-radius: 8px;
  background-color: #409eff;
  color: #fff;
}
</style>

这说明两件事:

  1. 你仍然是在写组件样式
  2. 只是样式部分由普通 CSS 换成了 SCSS 能力

3.3 单独使用 .scss 文件

除了写在组件内部,你也可以把样式拆到独立文件中:

src/
  ├── styles/
  │   ├── index.scss
  │   ├── variables.scss
  │   ├── mixins.scss
  │   └── reset.scss

例如在 main.ts 中引入全局样式:

import { createApp } from 'vue'
import App from './App.vue'
import './styles/index.scss'

createApp(App).mount('#app')

这就是企业项目中更常见的用法。


4. 最常用的 SCSS 语法

下面这些,才是你在 Vue 项目里最常用、最应该掌握的内容。

4.1 变量:统一管理设计令牌

变量是 Sass 最经典的能力之一。

$primary-color: #409eff;
$danger-color: #f56c6c;
$border-radius-base: 8px;
$spacing-base: 16px;

.card {
  padding: $spacing-base;
  border-radius: $border-radius-base;
}

变量最常见的使用场景:

  • 主色、成功色、危险色
  • 间距体系
  • 圆角体系
  • 阴影值
  • 字体尺寸

关键知识点:

变量不应该随便散落在业务组件里,而应该统一收敛到设计变量文件中管理。

4.2 嵌套:让样式层级更自然

SCSS 支持嵌套写法,这也是大家最直观能感受到的便利。

.user-card {
  padding: 16px;
  background: #fff;

  .title {
    font-size: 18px;
    font-weight: bold;
  }

  .desc {
    margin-top: 8px;
    color: #666;
  }
}

它非常适合写组件内部结构样式。

但这里有个非常重要的常见坑

嵌套不要过深。

不推荐这样写:

.page {
  .container {
    .content {
      .card {
        .title {
          color: #333;
        }
      }
    }
  }
}

原因是:

  • 选择器层级太深
  • 后期覆盖困难
  • 可维护性迅速变差

一般来说,嵌套控制在 2 到 3 层以内更合适。

4.3 &:引用当前选择器

在写状态类和 BEM 风格命名时,& 非常顺手:

.button {
  padding: 10px 16px;

  &:hover {
    opacity: 0.9;
  }

  &--primary {
    background-color: #409eff;
    color: #fff;
  }

  &--danger {
    background-color: #f56c6c;
    color: #fff;
  }
}

这在组件封装里非常常见。

4.4 mixin:复用一段样式逻辑

如果一段样式要反复出现,而且还可能带参数,就很适合抽成 mixin

@mixin flex-center($direction: row) {
  display: flex;
  flex-direction: $direction;
  justify-content: center;
  align-items: center;
}

.dialog-footer {
  @include flex-center();
}

.empty-state {
  @include flex-center(column);
}

mixin 非常适合:

  • Flex 布局复用
  • 文本省略
  • 按钮样式模板
  • 主题块样式复用

4.5 占位选择器 %@extend

如果你想复用一套纯样式结构,也可以使用占位选择器:

%ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.title {
  @extend %ellipsis;
}

.sub-title {
  @extend %ellipsis;
}

不过在团队项目里,很多人更偏向于用 mixin,因为 @extend 在复杂选择器合并时有时不够直观。

所以你可以先记住一个经验:

  • 通用样式逻辑复用:优先考虑 mixin
  • 简单静态样式复用:@extend 可以作为补充

5. @use 才是现代 Sass 的正确姿势

过去很多教程会写:

@import './variables.scss';
@import './mixins.scss';

但现在更推荐使用 @use

5.1 为什么不再推荐 @import

因为旧版 @import 存在一些问题:

  • 命名空间容易污染
  • 重复导入时不够清晰
  • 模块边界不明确

5.2 @use 的基本写法

例如我们有一个变量文件:

// styles/_variables.scss
$primary-color: #409eff;
$border-radius-base: 8px;

在组件样式中使用:

@use '@/styles/variables' as vars;

.login-btn {
  background-color: vars.$primary-color;
  border-radius: vars.$border-radius-base;
}

这里的 vars. 就是命名空间。

它的好处是:

  • 变量来源更清晰
  • 不容易和本地变量冲突
  • 项目规模变大后更容易维护

5.3 @use ... as * 什么时候可以用

你也可以这样写:

@use '@/styles/variables' as *;

这样变量就可以直接使用,不需要命名空间前缀。

虽然写起来更短,但在大型项目里不建议滥用,因为它又会带来“全局污染”风险。

更稳妥的建议是:

  • 小型项目可以适度用 as *
  • 中大型项目优先使用具名命名空间

这属于 Sass 里的关键知识点


6. Vue 项目里的样式模块拆分方案

Sass 真正有价值的地方,不只是“能写变量”,而是你怎么组织样式文件。

6.1 一套常见的目录结构

src/
  ├── styles/
  │   ├── index.scss          # 全局入口
  │   ├── reset.scss          # 样式重置
  │   ├── variables.scss      # 设计变量
  │   ├── mixins.scss         # 混入
  │   ├── functions.scss      # Sass 函数
  │   ├── common.scss         # 通用类
  │   └── theme.scss          # 主题相关

一个简单的 index.scss 可以这样组织:

@use './reset';
@use './common';

而在组件内部需要变量和混入时,按需引入:

@use '@/styles/variables' as vars;
@use '@/styles/mixins' as mixins;

.page-title {
  color: vars.$primary-color;
  @include mixins.flex-center();
}

6.2 什么应该放全局,什么应该放组件内部

建议这样划分:

适合全局文件的内容:

  • Reset / Normalize
  • 设计变量
  • 工具类
  • 主题色
  • 通用 mixin

适合组件内部的内容:

  • 当前组件的结构样式
  • 当前组件的状态样式
  • 当前组件的局部覆盖样式

这个边界如果不分清,项目后期非常容易出现“样式到处都有,但谁都不敢删”的情况。


7. 在 Vue 单文件组件里怎么写更合理

7.1 scopedscss 可以一起使用

最常见的写法是:

<style scoped lang="scss">
.user-panel {
  padding: 16px;

  .title {
    font-size: 20px;
  }
}
</style>

这表示:

  • 使用 SCSS 编写样式
  • 同时让样式限制在当前组件内

7.2 什么时候不加 scoped

以下场景常常不加 scoped

  • 全局样式文件
  • 主题变量覆盖
  • 第三方组件库样式覆盖
  • 通用工具类样式

例如你要覆盖 Element Plus 某些全局变量时,就通常不会使用 scoped

7.3 覆盖第三方组件样式时要注意什么

在 Vue 中,如果你使用了 scoped,有时需要借助深度选择器:

.user-form {
  :deep(.el-input__wrapper) {
    border-radius: 8px;
  }
}

这类写法在整合 Element Plus 时很常见。

但也要注意:

  • 不要把第三方组件库的覆盖样式写得过散
  • 最好集中到页面级或主题级样式中管理

8. Sass 和原生 CSS 变量,到底怎么选

这应该是现代前端里最现实的问题之一。

因为现在浏览器已经支持 CSS 自定义属性:

:root {
  --primary-color: #409eff;
}

.button {
  background-color: var(--primary-color);
}

那为什么还要 Sass 变量?

答案是:

它们解决的问题并不完全一样。

8.1 Sass 变量更适合“编译期管理”

Sass 变量在构建时就会被替换掉。

它更适合:

  • 管理静态设计令牌
  • 做样式模块复用
  • 写工具函数和 mixin
  • 做结构化样式组织

8.2 CSS 变量更适合“运行时切换”

CSS 变量最大的优势是:

它能在浏览器运行时动态变化。

它更适合:

  • 深色模式切换
  • 动态主题切换
  • 用户自定义主题色
  • JS 在运行时修改样式变量

例如:

:root {
  --primary-color: #409eff;
}

[data-theme='dark'] {
  --primary-color: #222;
}

8.3 最推荐的现实方案

在现代 Vue 项目里,一个非常实用的思路是:

  • Sass 负责样式组织、模块拆分、混入和静态变量
  • CSS 变量负责主题切换和运行时动态能力

这不是二选一,而是协作关系。

这也是本文最重要的关键知识点之一。


9. 常用知识点与关键知识点整理

为了帮助你快速建立体系,我们把 Sass 在 Vue 项目里的内容拆成两部分。

9.1 常用知识点

这些是你进入项目后最常碰到的:

  • 安装 sass
  • 在 Vite 中使用 .scss
  • 在 Vue 组件里写 <style lang="scss">
  • 变量
  • 嵌套
  • & 当前选择器引用
  • mixin@include
  • 样式文件拆分

9.2 关键知识点

这些决定你是不是“只会写语法”,还是“真的会做样式工程化”:

  • SCSS 才是 Vue 项目里的主流写法
  • 变量要统一收敛,不要散落到处都是
  • 嵌套不要过深
  • 优先使用 @use,不要再依赖老式 @import
  • Sass 变量和 CSS 变量不是互相替代关系
  • 全局样式、组件样式、主题样式必须分层管理

如果你把这些点掌握了,Sass 才真正变成了项目能力,而不是一堆零散语法。


10. 常见坑与避坑建议

10.1 把 Sass 当成“高级 CSS 炫技工具”

有些人学了 Sass 之后,最喜欢做的事就是疯狂嵌套、疯狂抽象。

这会直接导致:

  • 选择器层级爆炸
  • 代码阅读困难
  • 覆盖关系难以理解

记住:Sass 是为了降低复杂度,不是制造复杂度。

10.2 变量命名没有规范

不推荐:

$blue: #409eff;
$big-radius: 12px;

更推荐:

$color-primary: #409eff;
$radius-base: 8px;

变量命名最好体现“语义”,而不是只描述视觉颜色。

10.3 所有样式都写全局

如果项目一上来就把大量业务样式都写进全局 index.scss,后面很快就会失控。

建议始终坚持:

  • 全局负责规则和基础能力
  • 组件负责局部实现

10.4 继续使用过时的 @import

如果是新项目,尽量从一开始就使用 @use

这样后面不会再经历一轮 Sass 模块化迁移。


11. 一个适合 Vue 后台项目的落地建议

如果你正在做一个 Vue 3 后台项目,我更推荐这样的搭配:

  1. SCSS 做变量、混入和模块拆分
  2. scoped 管理大部分组件局部样式
  3. 用少量全局样式文件管理 Reset、工具类和主题变量
  4. 用 CSS 变量承接深色模式或主题切换
  5. 对第三方组件库的覆盖集中管理,不要零散散落

这样做的好处是:

  • 写起来顺手
  • 结构清晰
  • 组件边界明确
  • 后续接入主题切换也比较顺

12. 总结

Sass/SCSS 在现代 Vue 项目里,价值并不只是“多几个语法糖”。

它真正的作用是帮助你把样式从“零散拼凑”升级成“可维护的工程结构”。

你至少应该掌握这些核心认知:

  • SCSS 是 Vue 项目中的主流写法
  • sass 在 Vite 中接入很简单
  • <style lang="scss"> 是最常见的组件写法
  • 变量、嵌套、mixin@use、模块拆分是高频能力
  • Sass 更适合编译期管理,CSS 变量更适合运行时动态主题

如果你把这些点用在真实项目里,Sass 就不再只是“前端面试里提到过的技术名词”,而会真正变成你组织样式系统的一部分。