detached rulesets 是 less 中可赋值给变量、支持参数与延迟求值的未执行样式块,能作为 mixin 参数实现动态样式注入;必须用括号包裹并以分号结尾,调用时需加括号展开(如 @rules()),定义时捕获静态作用域变量。

Detached Rulesets 是什么,为什么能当 Mixin 参数用
Less 中的 detached ruleset 本质是一个未执行、可赋值给变量的样式块,它不是 CSS 输出,而是一个“样式函数”——支持参数、作用域、延迟求值。正因如此,它能被当作第一类值传给 Mixin,解决「动态注入样式逻辑」这类需求,比如条件化覆盖、主题切换、组件定制钩子。
常见错误是把它写成普通规则块(漏掉分号或括号),导致编译报错 Expected ')' got '{' 或直接被忽略。
- 必须用括号包裹整个规则集,并以分号结尾:
@my-ruleset: { color: red; font-size: 14px; }; - 不能在顶层直接展开(如
@my-ruleset();),必须通过 Mixin 调用或变量插值 - 它内部可访问调用时的作用域变量,但不继承父级选择器——这点和普通 Mixin 不同
把 Detached Rulesets 当作 Mixin 参数传递的写法
Mixin 接收 detached ruleset 参数,只需声明为变量名(无需类型标注),调用时直接传入规则集变量即可。关键点在于:**调用时要加括号展开**,否则只是引用,不会输出样式。
.apply-styles(@rules) {
@rules();
}
@hover-style: { &:hover { background: #eee; } };
.apply-styles(@hover-style);
上面代码会输出:.apply-styles:hover { background: #eee; }(注意:实际输出取决于调用上下文,此处假设在全局调用;若在选择器内调用,则生成嵌套)
- Mixin 内部用
@rules()执行,不是@rules(后者只是引用) - 传入的 ruleset 可含嵌套、媒体查询、甚至其他 Mixin 调用
- 不支持直接传入带参数的 ruleset(如
{ .btn(@size) { ... } }()),需先绑定参数或改用函数式封装
常见陷阱:作用域泄漏与变量覆盖
Detached ruleset 在定义时捕获其所在作用域的变量,但执行时又处于调用者作用域——这容易引发意料外的变量覆盖或未定义错误。
@color: blue;
@base-rules: { color: @color; };
.btn {
@color: red; // 这里重定义
@base-rules(); // 输出 color: blue(不是 red!)
}
因为 @base-rules 在定义时已绑定了外层 @color: blue,调用时不会重新解析变量。
- 想让 ruleset 使用调用时的变量?改用 Mixin 封装:
.base-rules() { color: @color; } - 需要组合多个 ruleset?用
+连接:@all: {@a(); @b();};,但注意顺序和嵌套层级 - 避免在 ruleset 内直接依赖外部未声明变量,尤其跨文件引入时
实用场景:主题配置 + 组件定制钩子
最典型的落地场景是「组件库提供样式钩子」:用户传入自定义 ruleset,组件内部在特定位置展开,实现无侵入定制。
.card(@custom-header: {}) {
&__header {
padding: 12px;
@custom-header();
}
&__body { font-size: 14px; }
}
@my-header-style: {
background: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
};
.card(@my-header-style);
这样既保持组件结构稳定,又允许深度样式干预。比单纯覆盖 CSS 选择器更可控,也比大量 @option 参数更灵活。
- 默认参数设为空 ruleset(
@custom-header: {})很关键,否则未传参会报错 - ruleset 内不要写顶层选择器(如
.foo { ... }),否则会污染全局输出 - 如果需多次复用同一 ruleset,建议用变量缓存,而非重复定义
真正难的不是语法,而是想清楚「哪些样式该由使用者决定」「哪些变量该暴露出去」——ruleset 看似自由,但随意开放作用域反而会让下游难以维护。










