vite-plugin-css-prefix-auto
v0.9.3
Published
Vite 插件:CSS 作用域隔离,解决微前端和模块联邦中的样式冲突问题
Downloads
2,666
Maintainers
Readme
vite-plugin-css-prefix-auto
Vite 插件:CSS 作用域隔离,解决微前端和模块联邦中的样式冲突问题。
功能特性
- ✅ CSS 作用域包裹:给 CSS 选择器外层包裹作用域类(
.btn→.appProcess .btn) - ✅ JSX 自动注入:自动给模块联邦暴露的组件父元素添加作用域 className
- ✅ HTML 自动注入:自动给
index.html的<body>添加作用域类名 - ✅ 精确处理:使用 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+)
安装
npm install vite-plugin-css-prefix-auto -D
# 或
pnpm add vite-plugin-css-prefix-auto -D使用方法
// 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'] // 排除的目录(可选)
})
]
});配置选项
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 <div>表单设计器</div>;
}
// 自动转换后(注入回调 ref)
function FormDesigner() {
const __scopeRefCallback = (__el) => {
if (__el) {
const __parent = __el.parentElement;
if (__parent) {
__parent.classList.add("appProcess");
__el.remove(); // 删除辅助 div
}
}
};
return (
<>
<div ref={__scopeRefCallback} style={{width: 0, height: 0, overflow: 'hidden'}}></div>
<div>表单设计器</div>
</>
);
}工作原理:
- 注入一个隐藏的辅助 div(宽高为 0,overflow: hidden)
- 通过回调 ref 在元素挂载时获取父元素
- 给父元素添加作用域 className
- 删除辅助 div
优势:
- ✅ 支持 Fragment 组件
- ✅ 支持任意 JSX 结构
- ✅ 支持 forwardRef、memo 等 HOC 包裹的组件
- ✅ 不修改原有组件的 className
- ✅ 作用域添加到组件的挂载容器上
- ✅ 无需导入 React hooks,性能更优
- ✅ 支持条件渲染场景
自动跳过的选择器
/* 这些选择器保持原样,不会被包裹 */
:root { --color: red; }
html { font-size: 16px; }
body { margin: 0; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }index.html 自动注入
<!-- 原代码 -->
<body>
<!-- 转换后 -->
<body class="appProcess">适用场景
- 模块联邦样式隔离:主应用(antd 5.x)和远端应用(antd 4.x)样式冲突
- 微前端样式隔离:多个子应用样式互不干扰
- 第三方组件库隔离:如 @designable 等组件库的样式隔离
- 远程组件自动注入:模块联邦暴露的组件自动添加作用域类名
技术原理
- CSS 处理:使用 PostCSS AST 解析 CSS/Less/SCSS,遍历所有规则,给选择器外层包裹
.scope父级 - JSX 处理:使用 Babel AST 解析 JSX/TSX,注入回调 ref,通过辅助 div 获取父元素并添加作用域 className
- HTML 注入:通过 Vite 的
transformIndexHtml钩子给<body>自动加上作用域类名 - React 组件检测:只处理返回 JSX 的 React 组件,跳过工具函数和配置对象
- 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 钩子
↓
给 <body> 添加作用域类名注意事项
- 作用域类名会自动添加到
<body>和组件的挂载容器上,确保样式能正确匹配 - 模块联邦远端应用的暴露组件会自动注入作用域逻辑
- Fragment 完全支持:可以使用 Fragment,插件会通过辅助 div 实现作用域隔离
- 条件渲染:所有 return 分支都会被处理,确保每个分支都有作用域逻辑
版本历史
v0.9.3 (2025-12-18)
- ✨ 新增:支持 forwardRef、memo 等高阶组件包裹的组件
- ⚡ 优化:使用回调 ref 替代 useRef + useEffect,性能更优
- ⚡ 优化:无需导入 React hooks,减少代码注入量
- 🐛 修复:条件渲染场景下 ref 为 null 的问题
v0.9.2
- 🐛 修复:TypeScript 类型定义问题
v0.9.0
- 🎉 重大重构:从 Babel 转换改为运行时 JS 注入
- ✨ 新增:支持 Fragment 组件
- ✨ 新增:通过辅助 div 实现作用域隔离
License
MIT
