冗余不是写得太多,而是复用方式错——@extend滥用、嵌套过深、跨文件继承失控,这三处踩中任意一个,css体积和维护成本都会指数级上升;应严格限制@extend仅用于语义纯粹的%占位符,嵌套不超过三层,且禁用跨文件未声明引用。

直接结论:冗余不是写得太多,而是复用方式错——@extend滥用、嵌套过深、跨文件继承失控,这三处踩中任意一个,CSS体积和维护成本都会指数级上升。
别让 @extend 变成选择器复印机
@extend 本质是编译时把多个选择器合并进同一条 CSS 规则,不是“共享样式”。一旦多个组件(比如 .btn、.link、.tag)都 @extend %text-truncate,最终输出就是 .btn, .link, .tag { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }。看着紧凑,实则埋雷:
- 若某处需要覆盖
white-space,就得写更重的选择器或加!important - 占位符里混了
margin或display,换场景就硬顶,违背原子性 - Dart Sass v7.0+ 默认禁用跨文件
@extend,漏掉@use "base"就直接报Extending unknown placeholder
安全做法:只对语义纯粹、永不单独存在、无参数、无媒体查询的原子规则用 % 占位符,例如 %sr-only、%clearfix;其余一律交给 @mixin。
嵌套别超三层,& 的作用域比你想的窄
Sass 嵌套不是缩进好看就行。编译后选择器权重飙升、DevTools 里找不到对应规则、改个 &--small 突然失灵,八成是嵌套失控了:
- 嵌套超过三层(如
.card { .header { .title { &--large { ... } } } }),生成的 CSS 选择器像.card .header .title--large,权重高且难覆盖 -
&永远只指“最近一层父选择器”,不是整个路径。在.btn { .icon { &--small { ... } } }里,&是.icon,不是.btn .icon - 想保留外层上下文?别靠猜,用变量暂存:
$block: ".btn"; #{$block} { #{$block}__icon { ... } }
子选择器(>)比空格后代选择器更可控,它明确切断意外继承,也避免生成过长选择器链。
PurgeCSS 不是补丁,是必须前置的设计环节
--style=compressed 只删空格和换行,不删逻辑冗余。真正管用的是在构建流程里接入 PurgeCSS,但它必须在 Sass 编译之后、最终打包之前运行:
- 输入必须是已生成的
.css文件 + 所有模板路径(src/**/*.{vue,js,ts,jsx}),漏一个,里面用的类就保留在 CSS 里 - Webpack 中顺序不能错:必须是
MiniCssExtractPlugin → PurgeCSS → CssMinimizerPlugin,否则 PurgeCSS 处理的是未提取的内联样式 - 开发环境别开 PurgeCSS——热更新时路径变动快,容易误删正在编辑的类
注意:PurgeCSS 对 @extend 生成的合并选择器照删不误,但删不掉源码里重复 @import "mixins" 导致的多次展开,这类问题得靠 stylelint-config-sass-guidelines 提前拦截。
真正容易被忽略的点
很多人以为“用了 % 占位符 + @extend”就万事大吉,但实际项目里,@extend 和嵌套混用、跨文件引用没配 @use、占位符里塞了 margin 或 @media,这些细节一出错,编译结果就不可控。最麻烦的是:它不报错,只悄悄产出一堆难以调试的长选择器——你得手动看 sass --style=expanded 输出才能发现。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











