我经常在技术社区看到类似的问题:“SASS 和 SCSS 不是一样的吗?”“SCSS 就是新版的 CSS 吧?”甚至有人直接把 SCSS 当成了 CSS 的替代品。这些说法要么只对了一半,要么完全跑偏了。作为一个从裸写 CSS 切到 SASS,再从 .sass 语法转投 .scss 语法,如今又在工程里深度使用 SCSS 的前端开发者,我准备把这三者之间的历史渊源、语法差异、编译机制和实际选型一次讲透。
这篇内容适合刚接触预处理器的初学者,也适合已经在用 SCSS 但对底层没什么概念的同学。它能帮你理清三者的边界,搞懂为什么工程里几乎都选 SCSS 而不是原生 SASS 语法,同时也会涉及安装、编译、@if 判断、工程配置这些高频实际场景。看完之后,你至少能在团队里把这个问题说清楚,不会再闹出“SCSS 和 SASS 哪个更好用”这种让人无从下嘴的争论。
1. 从 CSS 到 SASS:预处理器到底解决了什么问题
1.1 CSS 的痛点不是你写不快,而是维护不起
在聊 SCSS 和 SASS 之前,得先把时间轴拉回 CSS 最原始的形态。CSS 本身不是编程语言,它没有变量、没有函数、没有逻辑判断,也没有任何计算能力。你能做的就是写一堆选择器、声明属性和属性值,然后用层叠和优先级去控制页面外观。
举个例子,一个项目里主色、辅色、成功色、警告色,往往会出现在几十个甚至上百个文件里。如果产品经理说“主色换一下”,你得全局搜索替换,而且极容易漏掉某个写在奇怪位置的旧色值。
css复制/* 这是纯 CSS 的写法 */
.btn-primary {
background-color: #1890ff;
border-color: #1890ff;
}
.btn-primary:hover {
background-color: #40a9ff;
border-color: #40a9ff;
}
.card-title {
color: #1890ff;
}
.link {
color: #1890ff;
}
看上去每个规则都不复杂,但问题是:#1890ff 这个色值在代码里出现了 N 次,每次改动都要重复劳动。更麻烦的是嵌套结构,CSS 里要写父子关系选择器,必须手动拼出完整路径,一旦层级深了,类名一长串,可读性急剧下降。
当时前端开发者的普遍感受是:页面越来越复杂,CSS 却还停留在“能用但不好维护”的阶段。于是 2006 年,一个叫 Hampton Catlin 的人设计了 SASS,后来由 Natalie Weizenbaum 继续开发。SASS 的全称是 Syntactically Awesome Style Sheets,直译就是“语法上很棒的样式表”,核心目标就是给 CSS 加上变量、嵌套、混入、继承这些编程能力。
1.2 SASS 最初的缩进语法是怎么来的
早期 SASS 的语法非常有“性格”——它不写花括号,不写分号,完全靠缩进表示层级。这种风格和 Python 有点类似。整体写出来长这样:
sass复制$primary-color: #1890ff
.btn-primary
background-color: $primary-color
border-color: $primary-color
&:hover
background-color: lighten($primary-color, 10%)
注意几个细节:变量用的是 $ 开头,赋值语句后面没有分号;选择器之间靠换行和缩进区分父子关系;伪类 &:hover 里的 & 代表父选择器。这段代码最终会被编译成常规 CSS,浏览器只认编译后的结果。
这种语法在 2006 年前后是非常前卫的。它把“写样式”这件事带上了一点编程的味道。但也正因为语法太激进,对从 CSS 转过来的人很不友好——很多人习惯了花括号和分号,看到缩进语法第一反应就是“这也太反人类了”。
1.3 SCSS 的诞生:一次对 CSS 开发者的妥协与接纳
随着 SASS 逐步流行,问题也来了。虽然 SASS 解决了 CSS 的很多痛点,但它的缩进语法劝退了大量潜在用户。当时还有另一个预处理器叫 LESS,用的就是类 CSS 语法,学起来几乎零成本。
SASS 团队感受到了压力,于是在 SASS 3.0 版本推出了一个新的语法,叫 SCSS,全称 Sassy CSS。SCSS 的定位非常明确:保持 SASS 的全部能力,但把语法改成接近 CSS 的样子——有花括号、有分号、变量名和混入(Mixin)的写法也做了调整。
scss复制$primary-color: #1890ff;
.btn-primary {
background-color: $primary-color;
border-color: $primary-color;
&:hover {
background-color: lighten($primary-color, 10%);
}
}
如果你有 CSS 基础,这段代码几乎不需要学习成本,因为把 $primary-color 换成 #1890ff,这本身就是一份合法的 CSS。这也是 SCSS 后来成为主流选择的最关键原因:它向下兼容 CSS。任何一段合法的 CSS 代码,本身就是合法的 SCSS 代码。
所以准确说,SCSS 并不是 SASS 的“升级版”,而是 SASS 在 3.0 时代新增的第二种语法。SASS 这个名称同时指代整个预处理器工具,也包括了 SCSS 和原始的缩进语法。很多人搞混,根源就在这里:SASS 既是工具名,又是其中一种语法的名字。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. SCSS 与 SASS 语法差异:缩进派与花括号派的实战对比
2.1 同一个需求,两种写法
拿一个稍微完整的例子来对比。假设我们要定义按钮的默认样式和暗黑模式下的样式,包含变量、嵌套、混入和逻辑判断。
先看原始的 SASS 缩进语法:
sass复制$theme: dark
$btn-bg: #409eff
=button-variant($bg)
background-color: $bg
border: 1px solid darken($bg, 5%)
&:hover
background-color: lighten($bg, 10%)
.btn
+button-variant($btn-bg)
@if $theme == dark
color: #fff
@else
color: #333
再看 SCSS 写法:
scss复制$theme: dark;
$btn-bg: #409eff;
@mixin button-variant($bg) {
background-color: $bg;
border: 1px solid darken($bg, 5%);
&:hover {
background-color: lighten($bg, 10%);
}
}
.btn {
@include button-variant($btn-bg);
@if $theme == dark {
color: #fff;
} @else {
color: #333;
}
}
语法上的核心差异可以整理成一张表:
| 对比维度 | SASS(缩进语法) | SCSS(类 CSS 语法) |
|---|---|---|
| 文件扩展名 | .sass |
.scss |
| 花括号 | 不使用 | 必须使用 |
| 分号 | 不使用 | 必须使用 |
| 缩进 | 表达层级的关键 | 仅作可读性辅助 |
| Mixin 定义 | =name |
@mixin name |
| Mixin 引用 | +name |
@include name |
注释 // |
支持 | 支持 |
2.2 为什么工程里几乎不选 .sass 语法
我见过不少新人问我:“既然 .sass 语法更简洁,为什么不用它?”从写代码的角度,缩进确实能少敲几个字符,但它有几个很现实的问题。
第一,团队协作时,缩进语法的容错率特别低。花括号至少明确划定了代码块边界,代码格式化工具能自动修整;但缩进语法里,一个多余的空格或 Tab,可能直接改变选择器层级,而且报错信息不一定能精确定位到是缩进出了问题。
第二,工具链适配。目前主流编辑器的格式化、高亮、补全对 SCSS 的支持远比 SASS 缩进语法成熟。你随便搜一下 CSS 相关热词,会发现 scss 的讨论量远高于 sass 语法本身,生态偏向已经非常明显。
第三,也是最实际的,SCSS 可以拿 CSS 代码直接改后缀后再慢慢改造,迁移成本为零。而 .sass 语法的文件必须把整个结构改成缩进式,从存量 CSS 项目迁移时非常痛苦。
因此,除非你是个人小项目、且对缩进语法有偏好,否则我强烈建议新项目直接用 SCSS 写,把 .sass 当成一个历史遗留的语法而不是新选择。
2.3 SASS/SCSS 共同拥有但 CSS 没有的核心能力
既然 SCSS 只是 SASS 的语法外壳,那么 SASS 最重要的那些能力,在两种语法里是通用的。这里我把最常用、也最能提升生产力的特性梳理一下。
变量:用 $ 定义,支持数字、字符串、颜色、布尔值、列表、Map。可以存主题色、间距、断点,一处修改全局生效。
scss复制$primary: #1890ff;
$spacing-md: 16px;
$breakpoint-lg: 1200px;
嵌套:把父子选择器写进同一个层级结构里,代码结构跟 DOM 结构一一对应。结合 & 符号可以写伪类、伪元素、BEM 修饰符。
scss复制.card {
padding: $spacing-md;
&__title {
font-size: 18px;
}
&--active {
border-color: $primary;
}
}
混入(Mixin):把一组可复用的样式封装成函数样式,可以传参,也可以带默认值。适合处理兼容性前缀、按钮变体、文本省略等场景。
scss复制@mixin ellipsis($lines: 1) {
overflow: hidden;
text-overflow: ellipsis;
@if $lines > 1 {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
} @else {
white-space: nowrap;
}
}
继承:@extend 可以让一个选择器继承另一个选择器的全部样式,编译时会合并选择器,减少重复代码。使用时要注意别滥用,否则选择器会异常膨胀。
函数与逻辑:@if、@else if、@each、@for、@while 都能用,配合数值计算、颜色函数(lighten、darken、mix 等)和控制指令,可以做非常复杂的样式生成逻辑。
举个例子,用 @each 批量生成工具类:
scss复制$colors: (
primary: #1890ff,
success: #52c41a,
warning: #faad14,
danger: #ff4d4f,
);
@each $name, $color in $colors {
.text-#{$name} {
color: $color;
}
.bg-#{$name} {
background-color: $color;
}
}
编译后就会生成 8 个类,以后如果新增一个颜色,只需要在 Map 里加一行,工具类自动生成。这就是纯 CSS 永远做不到的量产能力。
3. SCSS 与 CSS 的真实关系:不是替代,而是编译
3.1 浏览器只认识 CSS,SCSS 只是生产工具
很多初学者会有一个误解:我写了 SCSS,浏览器是不是就能直接解析?不是的。浏览器不认识 SCSS,也不认识 SASS。它们只是源代码和中间态,必须在开发阶段通过预处理器编译成标准 CSS,浏览器才能渲染。
这个关系可以类比成:CSS 是最终交付物,SCSS 是加工蓝图。你用 SCSS 写下逻辑、变量、嵌套,然后构建工具读取 .scss 文件,执行编译任务,输出一个或多个 .css 文件。浏览器拿到的是编译后的产物,对编译前的 SCSS 代码一无所知。
理解这一点很重要,因为它直接影响你的调试思路。当样式异常时,不要打开 DevTools 找 SCSS 代码行——你应该看编译后的 CSS 和 Source Map 映射。Source Map 的作用,就是把编译后的 CSS 关系映射回 SCSS 源文件,从而能在浏览器 DevTools 里定位到原始的 SCSS 行号。如果没有 Source Map,调试嵌套层级多的 SCSS 会非常痛苦。
3.2 编译工具链与安装方式
SCSS 的编译工具经历了几个阶段。最早是 Ruby SASS,需要装 Ruby 环境,还慢;后来 LibSass 出现,也就是 node-sass,用 C++ 实现,性能提升不少,但跟进新语法版本永远慢半拍;现在已经全面进入 Dart Sass 时代,它由 SASS 官方团队用 Dart 语言开发,提供了 JS API,性能和同步速度都比较理想。
在现代前端工程里,你基本不需要关心底层编译细节,只需要装对包、配好构建工具。最常见的安装方式是这样的:
bash复制npm install -D sass
这里的 sass 就是 Dart Sass 的 npm 包名。注意,别再去新项目里用 node-sass 了。node-sass 基于 LibSass,官方已经进入维护停止状态,新版本依赖安装也容易出问题,编译速度也没有优势。如果你正在维护老项目,建议逐步迁移到 Dart Sass。
在 Vite 工程里,SCSS 几乎做到了开箱即用,安装完 sass 包后,直接在 .vue 文件里写 <style lang="scss">,或者在入口文件里引入 .scss 文件,Vite 内置的 CSS 处理器会自动调用 sass 完成编译。
vue复制<style lang="scss" scoped>
.container {
display: flex;
gap: 16px;
.item {
flex: 1;
}
}
</style>
如果你想把某个 SCSS 变量文件在整个工程里全局注入,不想在每个组件里手动 @import 一遍,可以在 Vite 配置里这样写:
javascript复制// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
}
});
这里 additionalData 会在每个 SCSS 文件编译前先插入这段导入代码,变量和混入就全局可用了。不过要小心,它也会插入到所有 SCSS 文件里,包含你没有计划注入的文件,所以尽量在里面只放变量、函数、混入这类不产出实际 CSS 的内容,避免样式被重复生成。
3.3 编译后的真实输出:拿着放大镜看 SCSS 如何变成 CSS
为了让 SCSS 的“编译”不被当成玄学,我拿一段实际代码出来,展示编译前后的对比。
假设你写了这样一段 SCSS:
scss复制$base-space: 8px;
$theme: dark;
.container {
padding: $base-space * 2;
.card {
border-radius: 4px;
@if $theme == dark {
background-color: #1f1f1f;
color: rgba(255, 255, 255, 0.85);
} @else {
background-color: #fff;
color: rgba(0, 0, 0, 0.85);
}
}
}
经过 Dart Sass 编译后,生成的是:
css复制.container {
padding: 16px;
}
.container .card {
border-radius: 4px;
background-color: #1f1f1f;
color: rgba(255, 255, 255, 0.85);
}
注意几个变化。$base-space * 2 在编译阶段就被算成了 16px;@if 判断被展开,dark 模式下只保留 dark 分支的样式,不会把 else 分支也输出;嵌套结构被拍平,变成 .container .card 这种普通后代选择器;变量全部被替换成实际值。编译后的 CSS 里没有任何 SCSS 痕迹。
这说明了两件事:SCSS 是在编译时静态处理逻辑的,不是运行时;变量和条件判断不会增加浏览器运行时的负担,所有开销都发生在构建阶段。这也是为什么 SCSS 即使引入编程特性,在浏览器端的表现也不会比手写 CSS 差。
3.4 从 CSS 反向迁移的思路
如果你维护的是一个纯 CSS 老项目,想分步引入 SCSS,最平滑的方式是:先只把文件后缀改成 .scss,由于 SCSS 兼容 CSS,代码一行不用动也能编译通过。然后再逐步抽变量、提嵌套、建混入。千万别一上来就大重构,那样容易在动样式的同时引入一堆回归问题。你可以在每次改动后跑一次 diff,确认编译生成的 CSS 跟原 CSS 一致,再继续下一步。
4. 工程落地:现代项目里怎么选、怎么配、怎么写
4.1 为什么现在团队普遍用 SCSS 而不是 SASS 缩进语法或纯 CSS
抛开个人偏好不谈,工程落地时选型核心看三点:生态成熟度、协作友好度、迁移成本。
生态方面,主流的 UI 组件库(例如 Ant Design Vue、Element Plus)的样式源码和定制方案,基本都提供 SCSS 版本;各类脚手架和构建工具对 SCSS 的适配最彻底,Vite、Webpack、Rollup 都有稳定的 loader 或预处理器配置。SASS 缩进语法虽然也能编译,但很多配套工具、代码片段、自动修复插件都优先支持 SCSS。
协作方面,SCSS 的代码格式化有标准答案,团队里用 Stylelint 和 Prettier 可以自动统一风格;而缩进语法在多人协作时很容易因为 Tab 和空格混用出现不可控的嵌套错误。
迁移成本前面也说过:SCSS 兼容 CSS,所以老项目直接改后缀就完成第一步。SASS 缩进语法则要求重写整个文件的格式,投入产出比不划算。
4.2 场景化选型表:什么时候用 CSS、什么时候用 SCSS
不是所有项目都必须上 SCSS,也不是所有场景都适合用 SCSS。我建议你按下面这张表来判断:
| 项目场景 | 推荐选择 | 理由 |
|---|---|---|
| 单页面或一次性活动页 | 纯 CSS | 代码量小,不需要变量和逻辑,减少构建配置 |
| 企业级中后台应用 | SCSS | 主题定制、状态逻辑、规模化管理需求高 |
| 组件库发版 | SCSS | 通过变量可变肤色、通过混入快速生成变体 |
| 快速原型 Demo | 纯 CSS 或 SCSS 均可 | 怎么快怎么来,重点是验证交互 |
| Tailwind 等原子化 CSS 项目 | 主要写工具类,少量 CSS 变量 | 原子化 CSS 本身已经解决了复用问题,SS 用于少量全局 token |
顺便提一下热搜词里的“原子性 css”“css grid”“css flex”,这些和 SCSS 并不冲突。原子化 CSS(如 Tailwind)提供的是工具类优先的写法,SCSS 提供的是样式组织能力,你完全可以在一个项目里用 Tailwind 管大部分布局和样式,再用 SCSS 维护少量全局主题变量和自定义混入。SCSS 里的 @include、@each 甚至可以帮助你批量生成带语义的工具类,像前面举的 .text-primary 例子就是这种思路。
4.3 SCSS 写法的工程规范建议
这里分享几条我踩过坑之后的规范习惯。
嵌套层级不超过 3 层。嵌套多了,编译出来的选择器会又长又深,比如 .nav .list .item .link .icon 这种,一是可读性差,二是浏览器匹配时效率下降。我的建议是:最多嵌套到 4 层,超过就考虑用 BEM 或抽子组件。
变量统一丢进 variables.scss,混入丢进 mixins.scss,按功能分文件。文件维护上不要把所有东西堆进一个 style.scss 里。可以这样组织:
text复制styles/
├── variables.scss # 所有设计变量
├── mixins.scss # 可复用混入
├── functions.scss # 自定义函数
├── reset.scss # 全局重置
└── index.scss # 统一入口
@import 和 @use 的选择上,新项目一律用 @use。旧版 @import 在 Dart Sass 里已经在走废弃流程,@use 有命名空间、不会重复引用多次编译,更干净。如果你看网上老教程还在用 @import,可以自动把它替换成 @use 的思路。
scss复制@use './variables' as *;
@use './mixins' as *;
as * 表示引入所有成员且不带命名空间前缀,写起来和旧的 @import 差不多。但如果不加 as *,你访问变量时就要写成 variables.$primary,可读性也还行,看你习惯。
4.4 一个能直接抄作业的完整示例
假设你现在要做一个暗黑模式适配,要维护一组主题变量,并且按钮在不同主题下表现不同。用 SCSS 可以这样写:
scss复制// variables.scss
$light-theme: (
bg-color: #ffffff,
text-color: #333333,
primary-color: #1890ff,
border-color: #d9d9d9,
);
$dark-theme: (
bg-color: #141414,
text-color: rgba(255, 255, 255, 0.85),
primary-color: #177ddc,
border-color: #434343,
);
// mixins.scss
@mixin themed($map) {
@each $key, $value in $map {
#{$key}: $value;
}
}
// button.scss
@use './variables' as *;
@use './mixins' as *;
.app-root {
@include themed($light-theme);
&.dark {
@include themed($dark-theme);
}
}
.btn {
background-color: var(--primary-color);
border: 1px solid var(--border-color);
color: var(--text-color);
@if map-get($dark-theme, 'primary-color') {
&:hover {
opacity: 0.85;
}
}
}
这里用到了 SCSS 的 Map 变量、@each 循环、@mixin 传参和 CSS 自定义属性输出。最终效果是:通过在根节点切换 .dark 类,整个页面的主题变量会同步切换。SCSS 在编译期间生成了一组 CSS 变量,运行时的主题切换交给 CSS 变量本身完成,性能更好,也弥补了 SCSS 变量“编译后就消失”的短板。
5. 从安装到上线:我踩过的坑和值得记住的细节
5.1 安装和编译阶段最容易踩的坑
关于“scss 安装”,很多新手会在装包阶段被老教程带偏,装了 node-sass,然后在新版 Node.js 环境下报各种 binding 编译错误。我在 2022 年接手过一个老后台项目,锁定的 node-sass 版本根本不支持当时的 Node 版本。后来统一改用 sass(Dart Sass)才顺利跑通。
如果项目里有全局安装的历史包袱,可以先清理再装:
bash复制npm uninstall node-sass
npm install -D sass
装完之后注意检查 package.json 里的版本,确保 sass 在 devDependencies 里,因为它是构建期工具,不需要打到生产环境依赖。
5.2 编译阶段报错:css minification error 的排查思路
热搜词里有一条很典型的报错:error: css minification error: cannot read properties of undefined (reading '...')。这个问题不是 SCSS 语法本身错了,而是压缩阶段出了问题。
简单解释一下:你的 SCSS 先被编译成 CSS,然后构建工具(比如 Vite、Webpack)会对生成的 CSS 做 minify,也就是压缩去空格。压缩器解析到一个非法的 CSS 结构或新语法时,可能就会报这种听起来很诡异的错误。
我遇到过一次,是因为 SCSS 里写了十六进制颜色简写:#ab 这种无效颜色。Sass 编译时居然没有直接报错,但压缩器解析时读不到完整颜色定义,直接崩溃。排查的方法是:暂时关闭 CSS minify,把编译后的 CSS 单独拿出来交给压缩器跑,通常会得到更具体的行号。在 Vite 里可以临时配置:
javascript复制// vite.config.js
export default defineConfig({
build: {
cssMinify: false
}
});
编译通过后,再用暴露出的报错信息反查 SCSS 源文件,基本就能定位到是哪个表达式生成了非法 CSS。
5.3 嵌套与选择器性能:不要为了“看着舒服”牺牲可维护性
SCSS 的嵌套能力确实好用,但很多新手会把嵌套当成唯一组织代码的方式,结果一个文件里所有样式都套在一个根选择器下面。这样编译之后,选择器路径非常深。
比如:
scss复制.page {
...
.header {
...
.nav {
...
.nav-item {
...
.nav-link {
...
}
}
}
}
}
编译后就是 .page .header .nav .nav-item .nav-link。这种选择器的特异性极高,而且后续要覆盖它必须写同样深度的选择器。更关键的是,html 结构和 css 耦合过深,一旦 DOM 结构调整,样式就会崩。
我的处理方式是:尽量用 BEM 类名搭配两层以内的嵌套,把 &__ 和 &-- 用起来。两层嵌套既能保证代码紧凑,又不会造成不可控的选择器深度。
5.4 SCSS 变量和 CSS 自定义属性的分工
很多人刚开始接触 CSS 变量(CSS Custom Properties)后,会纠结到底该用 SCSS 变量还是 CSS 变量。结论很简单:如果是编译期固定值,比如颜色、字体、间距的设计令牌,用 SCSS 变量,编译后直接拍平成最终值;如果是运行时要动态切换的值,比如主题切换、DOM 内联变量,用 CSS 自定义属性。
更高级的做法是两者配合:SCSS 变量负责编译期生成一套 CSS 变量的初始值,运行时通过切换类名或 JavaScript 修改变量值实现主题切换。这样既有 SCSS 的编程能力,也可以保留 CSS 变量运行时的灵活性。前面 4.4 的示例就是这种配合思路。
5.5 团队协作中的规范:宁可多花时间统一,也别让样式失控
在我待过几个前端团队后,发现 SCSS 项目最容易出问题的不是技术,而是规范不一致。有人用嵌套,有人全写平铺;有人用 @mixin,有人直接 @extend;有人把变量放在默认的 variables.scss,有人顺手写在组件里。最终结果就是:样式文件越来越难维护,旧代码没人敢动。
所以强烈推荐在项目里引入 Stylelint 的 SCSS 规则集,用 stylelint-config-standard-scss 或 stylelint-scss 插件,配合 Prettier 一起使用,把嵌套深度、@import 和 @use 的用法、重复选择器这类问题直接通过自动化检查拦下来。从长期来看,这比我手写代码时需要的注意力成本低得多。
6. 这个能力往后还能怎么扩展
到这里,SCSS、CSS 和 SASS 的定位已经比较清晰了:CSS 是规范,SCSS 是 SASS 预处理器在 3.0 时代提供的主流语法,SASS 是整个预处理器的总称。选择上,前端工程里用 SCSS 几乎是唯一理性答案,但也要结合具体项目体量,别迷信“用了 SCSS 就高级”。
根据我自己的体会,从纯 CSS 切到 SCSS,最大的收获不是少写了多少代码,而是思维方式的转变——你开始把样式当作一门可以用变量、逻辑、函数去组织的“代码”来思考,而不是一条条静态的声明清单。这种思维一旦建立,再看原子化 CSS、CSS-in-JS、原生 CSS 嵌套标准这些新东西,就不会觉得它们彼此冲突,因为它们其实是在不同抽象层级上解决同一个问题的不同方案。
最后再分享一个实际小技巧:如果你在 DevTools 里调试 SCSS 编译后的样式,一定记得开启 Source Map。Vite 默认开启,Webpack 需要在 devtool 和 sass-loader 的 sourceMap 选项里配置。有了 Source Map,你在浏览器里点击样式规则时,能直接跳到编辑器里的对应 SCSS 行,不用再对着编译后的 CSS 头疼半天。这几乎是我从手写 CSS 转向 SCSS 之后,用得最多的一个调试功能。
