Vite 插件:CSS 作用域隔离,解决微前端和模块联邦中的样式冲突问题
npm install vite-plugin-css-prefix-autoVite 插件:CSS 作用域隔离,解决微前端和模块联邦中的样式冲突问题。
- ✅ CSS 作用域包裹:给 CSS 选择器外层包裹作用域类(.btn → .appProcess .btn)
- ✅ JSX 自动注入:自动给模块联邦暴露的组件父元素添加作用域 className
- ✅ HTML 自动注入:自动给 index.html 的 添加作用域类名
- ✅ 精确处理:使用 PostCSS + Babel AST 解析,不会误伤代码
- ✅ 多格式支持:支持 CSS、Less、SCSS、Sass、JSX、TSX
- ✅ 智能过滤:自动跳过 :root、html、body、@keyframes 等全局选择器
- ✅ Fragment 支持:支持 Fragment 组件,通过辅助 div 实现作用域隔离
- ✅ HOC 支持:支持 forwardRef、memo 等高阶组件包裹的组件(v0.9.3+)
- ✅ 性能优化:使用回调 ref 替代 useRef + useEffect,性能更优(v0.9.3+)
``bash`
npm install vite-plugin-css-prefix-auto -D或
pnpm add vite-plugin-css-prefix-auto -D
`typescript
// vite.config.ts
import { defineConfig } from 'vite';
import cssScopePlugin from 'vite-plugin-css-prefix-auto';
export default defineConfig({
plugins: [
cssScopePlugin({
scope: 'appProcess', // 作用域类名
include: ['src/', 'node_modules/@designable/'], // 要处理的目录
exclude: ['node_modules/antd'] // 排除的目录(可选)
})
]
});
`
`typescript
interface CssScopePluginOptions {
/**
* 作用域类名(会加到 body 上,也会作为 CSS 选择器的父级)
* @example 'appProcess'
*/
scope: string;
/**
* 要处理的目录列表(相对于项目根目录)
* @example ['src/', 'node_modules/@designable/']
*/
include: string[];
/**
* 排除的目录或文件(可选)
* @example ['node_modules/antd']
*/
exclude?: string[];
}
`
`css
/ 原代码 /
.btn { color: red; }
.card, .panel { padding: 10px; }
.ant-modal { z-index: 1000; }
/ 转换后 /
.appProcess .btn { color: red; }
.appProcess .card, .appProcess .panel { padding: 10px; }
.appProcess .ant-modal { z-index: 1000; }
`
`jsx
// vite.config.ts 中配置了 federation exposes
federation({
exposes: {
'./FormDesigner': './src/views/FormDesigner/FormDesigner.jsx'
}
})
// 原代码 - FormDesigner.jsx
function FormDesigner() {
return
// 自动转换后(注入回调 ref)
function FormDesigner() {
const __scopeRefCallback = (__el) => {
if (__el) {
const __parent = __el.parentElement;
if (__parent) {
__parent.classList.add("appProcess");
__el.remove(); // 删除辅助 div
}
}
};
return (
<>
工作原理:
1. 注入一个隐藏的辅助 div(宽高为 0,overflow: hidden)
2. 通过回调 ref 在元素挂载时获取父元素
3. 给父元素添加作用域 className
4. 删除辅助 div
优势:
- ✅ 支持 Fragment 组件
- ✅ 支持任意 JSX 结构
- ✅ 支持 forwardRef、memo 等 HOC 包裹的组件
- ✅ 不修改原有组件的 className
- ✅ 作用域添加到组件的挂载容器上
- ✅ 无需导入 React hooks,性能更优
- ✅ 支持条件渲染场景
$3
`css
/ 这些选择器保持原样,不会被包裹 /
:root { --color: red; }
html { font-size: 16px; }
body { margin: 0; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
`$3
`html
`适用场景
1. 模块联邦样式隔离:主应用(antd 5.x)和远端应用(antd 4.x)样式冲突
2. 微前端样式隔离:多个子应用样式互不干扰
3. 第三方组件库隔离:如 @designable 等组件库的样式隔离
4. 远程组件自动注入:模块联邦暴露的组件自动添加作用域类名
技术原理
1. CSS 处理:使用 PostCSS AST 解析 CSS/Less/SCSS,遍历所有规则,给选择器外层包裹
.scope 父级
2. JSX 处理:使用 Babel AST 解析 JSX/TSX,注入回调 ref,通过辅助 div 获取父元素并添加作用域 className
3. HTML 注入:通过 Vite 的 transformIndexHtml 钩子给 自动加上作用域类名
4. React 组件检测:只处理返回 JSX 的 React 组件,跳过工具函数和配置对象
5. HOC 支持:递归检测 forwardRef、memo 等高阶组件,提取内部真实组件函数进行处理工作流程
`
1. configResolved 钩子
↓
提取 vite.config 中的 federation.exposes 配置
↓
2. transform 钩子(处理 JSX/TSX)
↓
检查文件是否在 exposes 列表中
↓
使用 Babel 解析 AST
↓
检测所有 export 的内容(支持 forwardRef/memo 等 HOC)
↓
过滤出返回 JSX 的 React 组件
↓
注入回调 ref + 辅助 div
↓
3. PostCSS 插件(处理 CSS)
↓
给选择器包裹作用域前缀
↓
4. transformIndexHtml 钩子
↓
给 添加作用域类名
`注意事项
1. 作用域类名会自动添加到
` 和组件的挂载容器上,确保样式能正确匹配MIT