237 lines
4.9 KiB
Markdown
237 lines
4.9 KiB
Markdown
# 微前端架构中样式隔离与共享的方案及最佳实践
|
||
|
||
在微前端架构中,样式隔离与共享需要兼顾 "模块间样式不冲突" 和 "公共样式高效复用",可以结合以下方案实现:
|
||
|
||
### **一、样式隔离方案**
|
||
|
||
确保各微应用的样式不会相互污染,推荐 3 种实用方案:
|
||
|
||
#### 1. **CSS Modules(最常用)**
|
||
|
||
* 原理:通过 Webpack/Vite 的 CSS Modules 插件,将类名编译为唯一哈希值(如`header`→`_header_1234_`)
|
||
|
||
* 实现:
|
||
|
||
```
|
||
// 组件中使用
|
||
|
||
import styles from './Product.module.css'
|
||
|
||
const Product = () => (
|
||
|
||
  \<div class={styles.productContainer}>
|
||
|
||
  \<h2 class={styles.productTitle}>商品列表\</h2>
|
||
|
||
  \</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) => {
|
||
|
||
  // 创建带Shadow DOM的容器
|
||
|
||
  const container = document.createElement('div')
|
||
|
||
  const shadowRoot = container.attachShadow({ mode: 'closed' })
|
||
|
||
  
|
||
|
||
  // 将微应用挂载到shadowRoot
|
||
|
||
  document.body.appendChild(container)
|
||
|
||
  loadMicroApp(appName, shadowRoot)
|
||
|
||
}
|
||
```
|
||
|
||
* 注意:Shadow DOM 会隔离大部分样式,但可能影响字体图标、全局样式(如`body`)
|
||
|
||
#### 3. **命名空间隔离(简单直接)**
|
||
|
||
* 原理:为每个微应用约定独特的前缀(如`app-product-`),所有样式类名均带前缀
|
||
|
||
* 实现:
|
||
|
||
```
|
||
// app-product模块的样式
|
||
|
||
.app-product {
|
||
|
||
  &-container { padding: 20px; }
|
||
|
||
  &-title { font-size: 18px; }
|
||
|
||
}
|
||
```
|
||
|
||
```
|
||
// 对应组件
|
||
|
||
const Product = () => (
|
||
|
||
  \<div class="app-product-container">
|
||
|
||
  \<h2 class="app-product-title">商品列表\</h2>
|
||
|
||
  \</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 {
|
||
|
||
  padding: 8px 16px;
|
||
|
||
  background: \$primary-color;
|
||
|
||
}
|
||
```
|
||
|
||
* 通过模块联邦导出样式文件:
|
||
|
||
```
|
||
// app-shared/vite.config.ts
|
||
|
||
export default defineConfig({
|
||
|
||
  plugins: \[
|
||
|
||
  federation({
|
||
|
||
  name: 'appShared',
|
||
|
||
  exposes: {
|
||
|
||
  './styles': './src/styles/global.scss',
|
||
|
||
  './variables': './src/styles/variables.scss'
|
||
|
||
  }
|
||
|
||
  })
|
||
|
||
  ]
|
||
|
||
})
|
||
```
|
||
|
||
* 其他应用引入使用:
|
||
|
||
```
|
||
// 在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 {
|
||
|
||
  \--primary-color: #42b983;
|
||
|
||
  \--border-radius: 4px;
|
||
|
||
}
|
||
```
|
||
|
||
* 所有微应用直接使用这些变量(无需导入,天然共享):
|
||
|
||
```
|
||
// 任意微应用的样式
|
||
|
||
.card {
|
||
|
||
  border: 1px solid var(--primary-color);
|
||
|
||
  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 生成) |