必须显式配置 sassOptions.outputStyle: 'compressed' 且禁用 sourceMap,否则 CSS 体积无法有效压缩;@extend、@import 和深层嵌套才是体积主因,压缩仅删空格注释,gzip 提升不足 3%。

必须显式配置 sassOptions.outputStyle: 'compressed'
sass-loader 默认输出 nested 格式,哪怕你配了 css-minimizer-webpack-plugin,它也只能压缩 Sass 已经生成的“胖”CSS——空格、换行、缩进早就在第一轮编译里塞进去了。不设这个,后续所有压缩都事倍功半。
配置位置很关键:
- Vue CLI 项目:写在
vue.config.js的css.loaderOptions.sass.sassOptions下 - 原生 Webpack:写在
module.rules.use[loaderIndex].options.sassOptions里(loaderIndex是sass-loader在 rules 数组里的下标) - 写错位置(比如放在
webpack.config.js顶层或optimization.minimizer里)会被完全忽略
验证是否生效:临时把 outputStyle 改成 'expanded',看编译出的 CSS 是否突然带缩进、空行、多层嵌套——如果是,说明配置已命中。
禁用 sourceMap: false 是硬性要求,不是可选项
开启 sourceMap 会让每个 SCSS 文件生成 base64 编码的映射内容,直接追加在最终 CSS 文件末尾。中等项目轻松增加 80–150 KB,gzip 几乎不压缩——因为 base64 是高熵内容。这不是调试开关,是体积暴增源。
立即学习“前端免费学习笔记(深入)”;
别信某些文档说“新版默认关闭”,dart-sass v1.82.0 仍默认 true;MiniCssExtractPlugin 不干预 sass-loader 是否注入 map,所以必须在 sassOptions 层关掉。
开发调试需求应由 Webpack 的 devtool: 'source-map' 统一提供 JS 层 sourcemap,样式定位足够覆盖。
additionalData 里藏了 @debug 或 @warn?上线就静默失效
当你用 additionalData 注入全局变量或 mixin 时,如果里面含 @debug 或 @warn,它们在 compressed 模式下会被 Sass 编译器直接丢弃——你本地看到日志,上线却毫无反应,容易误判逻辑没执行。
更隐蔽的问题是:additionalData 内容本身不参与压缩逻辑,但会进入编译上下文。若含大量注释、未使用的占位样式或条件判断残留,也会被保留在输出中。
推荐做法:
- 把全局变量抽成独立的
_variables.scss,用@import或@use引入,而非字符串拼接注入 - 生产构建前清理所有
@debug/@warn,或用环境判断包裹:@if $env == 'dev' { @debug 'xxx' } - 调试逻辑单独抽到
_debug.scss,仅在 dev 环境@use,不参与 production 构建
真正卡体积的从来不是空格,而是 @extend、@import 和循环
--style=compressed 只删空格换行注释,不合并重复声明、不剔未用选择器、不简写数值颜色。实测 gzip 提升通常不到 3%,而一个失控的 @extend %btn-base 在 5 个上下文调用,就会生成 .header .btn、.modal .btn 等 5 条独立规则。
快速定位问题:
- 运行
grep -o "margin:" main.css | wc -l,如果数字远高于组件数(比如 >200),基本是嵌套或@extend泛滥 - 检查 CSS 文件末尾是否有大量 base64 字符串——那是
sourceMap在作祟 - 用
@use替代@import:后者是文本拼接,哪怕只调用px2rem(12),也会把整个_utils.scss全塞进输出
压缩只是收尾动作;体积问题必须往前推到 SCSS 编写和构建流程中解决。最易被忽略的是:你改了 sassOptions,但没同步清理源码里的深层嵌套、全量 @import 和暴力循环生成工具类。


















