Files
DDDMicroFrontend/doc/样式隔离与共享的方案.md
2025-09-24 23:56:55 +08:00

237 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 微前端架构中样式隔离与共享的方案及最佳实践
在微前端架构中,样式隔离与共享需要兼顾 "模块间样式不冲突" 和 "公共样式高效复用",可以结合以下方案实现:
### **一、样式隔离方案**
确保各微应用的样式不会相互污染,推荐 3 种实用方案:
#### 1. **CSS Modules最常用**
* 原理:通过 Webpack/Vite 的 CSS Modules 插件,将类名编译为唯一哈希值(如`header``_header_1234_`
* 实现:
```
// 组件中使用
import styles from './Product.module.css'
const Product = () => (
&#x20; \<div class={styles.productContainer}>
&#x20; \<h2 class={styles.productTitle}>商品列表\</h2>
&#x20; \</div>
)
```
* 在 Vue3+TSX 中,为样式文件添加`.module.css`/`.module.scss`后缀
* 在组件中通过`import styles from './xxx.module.css'`引入,使用`styles.className`绑定
#### 2. **Shadow DOM最强隔离**
* 原理:利用浏览器原生的 Shadow DOM 特性,将微应用的 DOM 树隔离在独立作用域
* 实现:在主应用加载微应用时,将其挂载到 Shadow 容器中
```
// 主应用加载微应用时
const mountMicroApp = (appName: string) => {
&#x20; // 创建带Shadow DOM的容器
&#x20; const container = document.createElement('div')
&#x20; const shadowRoot = container.attachShadow({ mode: 'closed' })
&#x20;&#x20;
&#x20; // 将微应用挂载到shadowRoot
&#x20; document.body.appendChild(container)
&#x20; loadMicroApp(appName, shadowRoot)
}
```
* 注意Shadow DOM 会隔离大部分样式,但可能影响字体图标、全局样式(如`body`
#### 3. **命名空间隔离(简单直接)**
* 原理:为每个微应用约定独特的前缀(如`app-product-`),所有样式类名均带前缀
* 实现:
```
// app-product模块的样式
.app-product {
&#x20; &-container { padding: 20px; }
&#x20; &-title { font-size: 18px; }
}
```
```
// 对应组件
const Product = () => (
&#x20; \<div class="app-product-container">
&#x20; \<h2 class="app-product-title">商品列表\</h2>
&#x20; \</div>
)
```
### **二、样式共享方案**
将公共样式(如主题色、组件库样式)高效共享给各微应用:
#### 1. **通过共享模块app-shared导出样式**
*`app-shared`中集中管理公共样式:
```
// app-shared/src/styles/variables.scss
\$primary-color: #42b983;
\$font-size-base: 14px;
// app-shared/src/styles/global.scss
@import './variables.scss';
.btn {
&#x20; padding: 8px 16px;
&#x20; background: \$primary-color;
}
```
* 通过模块联邦导出样式文件:
```
// app-shared/vite.config.ts
export default defineConfig({
&#x20; plugins: \[
&#x20; federation({
&#x20; name: 'appShared',
&#x20; exposes: {
&#x20; './styles': './src/styles/global.scss',
&#x20; './variables': './src/styles/variables.scss'
&#x20; }
&#x20; })
&#x20; ]
})
```
* 其他应用引入使用:
```
// 在main-app或app-product中
import 'appShared/styles'; // 引入全局样式
// 或在SCSS中使用变量
@import 'appShared/variables';
.local-class { color: \$primary-color; }
```
#### 2. **使用 CSS 变量定义主题**
* 在主应用的根样式中定义全局 CSS 变量:
```
/\* main-app/src/styles/theme.css \*/
:root {
&#x20; \--primary-color: #42b983;
&#x20; \--border-radius: 4px;
}
```
* 所有微应用直接使用这些变量(无需导入,天然共享):
```
// 任意微应用的样式
.card {
&#x20; border: 1px solid var(--primary-color);
&#x20; border-radius: var(--border-radius);
}
```
* 优势:可动态修改(如切换深色模式),通过 JS 操作`document.documentElement.style.setProperty('--primary-color', 'xxx')`
#### 3. **共享 UI 组件库样式**
如果各应用使用同一 UI 库(如 Element Plus可在`app-shared`中统一引入,避免重复打包:
```
// app-shared/src/index.ts
import 'element-plus/dist/index.css';
// 导出UI组件
export { ElButton, ElInput } from 'element-plus';
```
其他应用直接从`app-shared`导入组件,无需重复引入样式。
### **三、最佳实践建议**
1. **优先级选择**
* 隔离:优先用 CSS Modules平衡易用性和隔离性特殊场景用 Shadow DOM
* 共享:基础变量用 CSS 变量,复杂样式通过`app-shared`导出
1. **避免全局污染**
* 禁止在微应用中使用无限制的全局选择器(如`div { ... }`
* 必要的全局样式(如重置样式)放在主应用,或通过`app-shared`统一导出
1. **构建配置**
* 确保 Vite 配置中`css.modules`开启(默认开启),避免类名冲突
* 对共享的 SCSS 变量,可配置`css.preprocessorOptions`简化导入路径
通过以上方案,既能保证各微应用样式独立,又能高效复用公共样式,维持架构的灵活性和一致性。
> (注:文档部分内容可能由 AI 生成)