真正起效的隔离是HTML容器class、构建时模块约束与运行时DOM挂载限制三者咬死;若根容器未正确挂载class="app-a",Less嵌套再规范也无效。

Less嵌套本身不产生作用域隔离
写 .app-a { .btn {} } 不等于样式隔离,它只是生成一条普通 CSS 规则 .app-a .btn,而这条规则是否生效,完全取决于 DOM 中是否存在带有 class="app-a" 的祖先容器。如果子应用的弹窗通过 ReactDOM.createPortal 挂载到 document.body,那 .app-a 根本不包裹它,样式直接失效。
常见错误现象:
- 主应用已加载
normalize.css,子应用又引入了reset.css,两者都在,层叠顺序不可控 - 子应用用了
ant-modal,默认挂载到document.body,脱离命名空间容器 -
@import "antd.less"没加(reference),变量重复注入,颜色被后加载的覆盖
必须强制启用 CSS Modules 模式
Less 文件只有被构建工具识别为模块(module),才能参与类名哈希化或前缀注入。否则,.module.less 和 .less 在 css-loader 眼里没区别——都当普通 CSS 处理,嵌套被平铺,前缀被忽略。
关键配置项:
立即学习“前端免费学习笔记(深入)”;
- Webpack 中
css-loader的modules.auto必须设为true,或显式匹配/\.module\.(less|css)$/ - 所有 Less 文件命名强制为
xxx.module.less,否则modules模式不触发 - 动态拼接的
className(如className={`btn ${type}`})必须走styles[type]映射,否则type字符串会作为全局 class 注入 - 要用
:global(.ant-btn)保留第三方类名,该语句必须写在.module.less文件中,否则哈希化后图标/样式丢失
HTML 容器 class 是隔离生效的前提条件
再规范的 Less 写法,如果根容器没挂对 class,就等于没写。这个 class 必须是子应用整个 DOM 树的唯一顶层容器 class,不能是 id,不能是属性选择器,也不能靠 JS 动态添加后才生效(因为样式已编译完成)。
典型逃逸路径:
- 子应用 JS 执行
document.head.appendChild(styleEl)—— 动态插入的<style></style>不经 Less 构建流程,也不受命名空间约束 - 使用
styled-components或emotion,其生成的样式直接写入document.styleSheets,绕过所有构建时处理 -
@font-face、@keyframes、CSS 变量(--primary-color)全局注册,加前缀无效 -
style="color: red"这类内联样式,Less 和任何编译时方案都管不了
变量与 Mixin 必须带前缀或参数化
Less 变量没有天然作用域,@primary-color 在任意文件重定义都会静默覆盖之前所有引用。命名空间块(如 .ui-kit { @primary-color: red; })对变量完全无效。
安全实践:
- 变量命名加模块前缀:
@btn-primary-bg、@header-title-color,杜绝泛义名 - 公共逻辑用参数化 mixin:
.btn-style(@type: default, @size: md),配合when守卫分支 -
@import (reference)能避免重复输出 CSS,但变量仍会注入全局作用域,所以 reference 文件里也别放通用变量,只放带前缀的专用变量 - 嵌套层级 ≤3 层,
&关系必须显式写出,避免隐式作用域误解
真正起效的隔离从来不是单靠 Less 嵌套,而是「HTML 容器 class + 构建时模块约束 + 运行时 DOM 挂载限制」三者咬死。最容易被跳过的那个 class="app-a",不在 DOM 上,Less 再规范也没用。


















