根本原因是Less默认不自动去重,每次@import都会重复展开mixin;应改用@import(once)管理变量、@import(reference)管理工具mixin,并禁用通配符导入。

为什么多个文件@import同一份公共.less会膨胀体积
根本原因是 Less 默认不做去重 —— 每个 @import "common/mixins.less" 都会把里面所有 mixin 展开一遍,哪怕 10 个组件都只调用 .flex-center(),最终 CSS 里就会出现 10 份一模一样的 display: flex; justify-content: center;。这不是压缩没开好,而是编译阶段就把重复逻辑塞进去了。
常见错误现象:main.css 里搜 flex-wrap: wrap 出现 27 次,但源码中只在 mixins/grid.less 定义了一次 .grid-row();查构建日志发现 header.less、card.less、list.less 全都 @import "mixins/grid.less",且各自调用了 .grid-row()。
-
@import (once)必须显式写,否则每次导入都算“新副本” - 路径必须完全一致:
@import "mixins/grid"和@import "./mixins/grid"被视为两个不同文件 - 变量和 mixin 不是“全局注册”,而是按导入顺序静态拼接,顺序错就失效
用 @import (once) + @import (reference) 精准控制输出
真正可控的组合是:@import (once) 管变量/基础类(必须真实输出),@import (reference) 管工具 mixin(不输出,只提供调用能力)。
比如你有一份 base/_variables.less 和 mixins/_utils.less:
立即学习“前端免费学习笔记(深入)”;
-
@import (once) "base/_variables.less";→ 确保@primary-color只定义一次,且最终 CSS 里能看见:root { --primary: #007bff; }这类声明 -
@import (reference) "mixins/_utils.less";→ 里面只有.px2rem(@px) { ... }这种纯计算 mixin,不产生任何 CSS,但你在.btn { width: .px2rem(16); }中能调用
验证是否生效:打开编译后 CSS,搜索 _variables.less 中某个变量生成的样式(如 color: #007bff),只应出现一次;而 _utils.less 里的 mixin 内容不应直接出现,只看得到调用后的计算结果(如 width: 1rem)。
避免 @import "components/*.less" 这类通配符导入
标准 Less 不支持通配符导入,Webpack 或 gulp-less 插件会静默加载匹配的所有文件,结果就是:你以为只引入了 button.less,实际连 old-datepicker.less 和 demo-modal.less 全被拉进来,还各自展开 mixin —— 体积暴涨且难以排查。
- 禁用写法:
@import "components/*.less"; - 改用显式列表:
@import "components/button.less"; @import "components/card.less"; - 更推荐方式:在入口
index.less中只@import (once)公共层,组件样式由 JS 动态 import 触发(Webpack + css-loader 支持 exportOnlyLocals)
如果项目已存在通配符导入,运行 lessc --verbose main.less 会打印出所有实际解析到的文件路径,可快速定位冗余项。
检查 :extend() 是否真在合并选择器,而非退化为复制
:extend() 是唯一能在编译期合并选择器、避免重复声明的方式,但它极易因路径或作用域问题退化成“假继承”——看着写了 .btn-primary:extend(.btn all),结果编译后还是孤立的 .btn-primary { ... } 块。
- 目标选择器必须真实输出:不能只靠
@import (reference)引入.btn定义,得确保它出现在某处正常@import的文件里 - 加
all才继承伪类::extend(.btn all)才能把.btn:hover也合并进来;漏掉就只剩基础样式 - 验证方式:在编译后 CSS 中搜索逗号分隔的选择器,如
.btn, .btn-primary, .btn-secondary { display: inline-flex; }—— 有这个,说明成功;只有.btn-primary { ... },说明链路断了
容易忽略的一点:一旦 .title:extend(%truncate) 和 .subtitle:extend(%truncate) 同时生效,它们就被焊死在同一组声明里,后续想单独覆盖 .title 就得用更高权重选择器,灵活性下降。需要局部微调的地方,老实用 mixin 更稳妥。


















