添加全局样式和 element ui框架

This commit is contained in:
编码猿
2025-09-24 23:56:55 +08:00
parent 21bf47c8a1
commit 537c7ca60e
37 changed files with 385486 additions and 8374 deletions

View File

@@ -0,0 +1,23 @@
# 完善微前端架构的建议
除了已有的主模块、订单联邦模块、共享资源模块以及 nacos 服务中心和 traefik api 网关外,还可以考虑以下几个方面来完善微前端架构:
* **状态管理与通信机制**:微前端架构中,各模块间的状态管理和通信是关键。可以采用 Pinia 等状态管理库,实现跨模块的数据共享。例如,通过在共享资源模块中定义
Pinia store然后在主模块和订单联邦模块中引入并使用确保所有微前端模块都能访问同一缓存存储实现数据共享。
* **路由管理**:虽然 @originjs/vite - plugin - federation
插件本身支持模块的动态加载,但仍需要合理设计路由系统,确保不同模块之间的路由切换顺畅。可以在主模块中统一管理路由,根据用户的访问路径,动态加载对应的微前端模块。例如,使用
Vue Router 的动态路由功能,根据不同的 URL 路径加载订单联邦模块或共享资源模块的组件。
* **样式隔离与共享**:需要确保各个微前端模块的样式不会相互干扰。可以采用 CSS Modules、Shadow DOM 或 CSS-in-JS
等技术来实现样式隔离。同时,对于一些共享的样式,可以在共享资源模块中进行定义,然后通过模块联邦的方式共享给其他模块。
* **错误处理机制**
:由于微前端模块是独立开发和部署的,可能会出现各种错误。需要建立统一的错误处理机制,捕获并处理微前端模块在加载和运行过程中出现的错误。例如,在主模块中设置全局的错误捕获器,当订单联邦模块或共享资源模块出现错误时,能够及时捕获并展示友好的错误提示信息。
* **性能优化**:微前端架构中,多个模块的加载可能会影响性能。可以通过代码分割、懒加载等方式来优化性能。@originjs/vite -
plugin - federation 插件本身支持代码分割,可将每个微前端模块的代码拆分成多个小块,只在需要时加载相应的代码块。此外,还可以使用缓存策略,减少重复加载。
* **安全机制**随着微前端模块的增加安全问题也需要重视。需要确保各个模块之间的数据传输是安全的防止跨站脚本攻击XSS、跨站请求伪造CSRF等安全漏洞。可以采用身份验证、授权、输入验证等安全措施来保障系统的安全。
> (注:文档部分内容可能由 AI 生成)

View File

@@ -1,10 +1,12 @@
### 依赖注意
DDD 中允许 “外层依赖内层”(交付层依赖领域层),但不允许 “内层依赖外层”。
- 正确features交付层 → domains领域层
- 错误domains领域层 → features交付层
#### 避免类型重复定义
如果不在视图中直接使用领域类型,就需要在交付层重新定义一套类似的类型(如 “视图专用商品类型”),这会导致:
- 代码冗余和不一致风险
@@ -12,14 +14,15 @@ DDD 中允许 “外层依赖内层”(交付层依赖领域层),但不允
- 业务规则分散(领域层的类型约束可能在视图层被忽略)
### 横切关注点
- 特征:横向,影响多个模块,那些无法放入任何一个业务模块,而是会"横着"贯穿多个甚至所有模块的技术性需求。
- 案例:日志记录、身份认证、授权、事务管理、异常处理、缓存、性能监控
## 业务关注点
- 特征:通常局限于一个业务模块内
- 案例:计算订单总额、验证用户邮箱格式、处理库存扣减
### 对象职责的设计理念对比:
#### 贫血模型
@@ -30,33 +33,33 @@ DDD 中允许 “外层依赖内层”(交付层依赖领域层),但不允
```typescript
// 1. 贫血的Order实体只有数据没有行为
class Order {
public id: string;
public items: OrderItem[];
public total: number; // 总额需要外部来计算和设置
public status: string;
public id: string;
public items: OrderItem[];
public total: number; // 总额需要外部来计算和设置
public status: string;
}
// 2. 所有业务逻辑都在外部的“服务”里
class OrderService {
calculateTotal(order: Order): void {
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
calculateTotal(order: Order): void {
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
order.total = total; // 从外部修改对象的数据
}
order.total = total; // 从外部修改对象的数据
}
markAsPaid(order: Order): void {
if (order.total <= 0) {
throw new Error('订单金额无效');
markAsPaid(order: Order): void {
if (order.total <= 0) {
throw new Error('订单金额无效');
}
order.status = 'paid'; // 从外部修改对象的状态
}
order.status = 'paid'; // 从外部修改对象的状态
}
}
// 使用方式
const order = new Order();
order.items = [...];
order.items = [ ... ];
const orderService = new OrderService();
orderService.calculateTotal(order); // 服务来算总额
orderService.markAsPaid(order); // 服务来改状态
@@ -70,42 +73,42 @@ orderService.markAsPaid(order); // 服务来改状态
```typescript
// 充血的Order实体数据 + 行为
class Order {
public id: string;
private _items: OrderItem[];
private _total: number; // 总额是内部计算的结果
public status: string;
public id: string;
private _items: OrderItem[];
private _total: number; // 总额是内部计算的结果
public status: string;
constructor(items: OrderItem[]) {
this._items = items;
this._total = this.calculateTotal(); // 构造时自己计算总额
this.status = 'created';
}
// 业务逻辑封装在实体内部
private calculateTotal(): number {
return this._items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
}
// 一个公开的业务方法
public markAsPaid(): void {
// 它自己 knows 自己的业务规则
if (this._total <= 0) {
throw new Error('订单金额无效,无法支付');
constructor(items: OrderItem[]) {
this._items = items;
this._total = this.calculateTotal(); // 构造时自己计算总额
this.status = 'created';
}
this.status = 'paid';
}
// 提供访问内部数据的方法(如果需要)
get total(): number {
return this._total;
}
// 业务逻辑封装在实体内部
private calculateTotal(): number {
return this._items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
}
get items(): ReadonlyArray<OrderItem> {
return [...this._items]; // 返回副本,保护内部数据
}
// 一个公开的业务方法
public markAsPaid(): void {
// 它自己 knows 自己的业务规则
if (this._total <= 0) {
throw new Error('订单金额无效,无法支付');
}
this.status = 'paid';
}
// 提供访问内部数据的方法(如果需要)
get total(): number {
return this._total;
}
get items(): ReadonlyArray<OrderItem> {
return [ ...this._items ]; // 返回副本,保护内部数据
}
}
// 使用方式:对象是聪明的,告诉它做什么就行,而不是一步步操作它
const order = new Order([...]);
const order = new Order([ ... ]);
order.markAsPaid(); // 对象自己处理状态变更
```

View File

@@ -0,0 +1,237 @@
# 微前端架构中样式隔离与共享的方案及最佳实践
在微前端架构中,样式隔离与共享需要兼顾 "模块间样式不冲突" 和 "公共样式高效复用",可以结合以下方案实现:
### **一、样式隔离方案**
确保各微应用的样式不会相互污染,推荐 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 生成)