@extend导致css膨胀,因其机械拼接选择器而非共享声明,造成选择器爆炸、跨文件污染及调试困难;安全用法仅限无上下文、无伪类、无媒体查询的%占位符,如%sr-only。

@extend 导致 CSS 代码膨胀,不是因为它“合并了样式”,而是它把多个选择器硬塞进同一组声明块,却完全不共享实际的 CSS 声明——浏览器仍要解析每一条规则,cssnano 也压不了重,gzip 后体积照样超标。
为什么 @extend 编译后 CSS 反而更大
它不复制声明,只拼接选择器。表面看“紧凑”,实则埋雷:
-
%button-base { padding: 0.5rem; border: 1px solid #ccc; }被.btn-primary和.btn-secondary同时@extend,输出是.button-base, .btn-primary, .btn-secondary { padding: 0.5rem; border: 1px solid #ccc; } - 一旦 HTML 中误加了
class="button-base",整套样式就全局生效,污染不可控 - 如果后续删掉
%button-base定义,但没检查所有@extend引用点,编译仍通过,CSS 里却残留一个永远不会被用到的.button-base选择器 - 搜索
padding: 0.5rem在输出 CSS 中出现 42 次?基本可判定是@extend泛滥,而非真实业务需要
@include 膨胀的是体积,@extend 膨胀的是选择器复杂度
@include 的问题是重复生成样式块;@extend 的问题是把选择器链越拉越长、越嵌套越危险:
- 在嵌套结构中用
@extend:.card { .header { @extend %text-sm; } }→ 编译出.card .header, .card .title, .modal .header等一堆带冗余上下文的选择器 - 跨媒体查询调用:
%btn-hover { &:hover { color: blue; } }被 5 个组件@extend→ 输出 5 组独立的.btn:hover、.card .btn:hover、.modal .btn:hover……组合爆炸 - 浏览器 CSS 解析是从右往左的,
.a .b .c .d .e这类长链在旧版 Safari 或 IE 中可能直接触发选择器长度限制 -
@include至少能精准定位:DevTools 点击.btn-primary,高亮的就是它自己的那条规则;@extend则高亮一整行选择器列表,根本分不清谁动了哪条
哪些 %placeholder 真的安全,哪些看似安全实则危险
安全的占位符必须满足:挂到任意 HTML 元素上都 100% 有效、无副作用、不依赖上下文:
- ✅ 安全:
%sr-only、%visually-hidden、%reset-list—— 不含伪类、无媒体查询、不随主题变量变化,直接加到<div> 上就起作用 <li>❌ 危险:<code>%btn-hover(含:hover)、%md\:flex(含媒体查询)、%card-header(隐含.card > &结构) - ⚠️ 验证法:把占位符名临时改成真实类(如
.sr-only),手动加到一个空<div class="sr-only"> 上,看是否正常工作且不影响其他样式 <li>Dart Sass 7.0+ 默认模块隔离,未显式 <code>@use的%placeholder会被静默忽略——构建不报错,但样式莫名缺失,排查成本极高
真正难处理的不是“该不该用 @extend”,而是它和 @mixin 混用时的组合爆炸:比如 mixin 内部又 @extend %base,Sass 会先展开 mixin,再对每个副本做 extend 合并——逻辑失控往往从这里开始。别信“适度使用”,协作项目里没有“适度”,只有白名单管控或彻底禁用。











