添加全局样式和 element ui框架
This commit is contained in:
23
doc/完善微前端架构的建议.md
Normal file
23
doc/完善微前端架构的建议.md
Normal 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 生成)
|
||||
97
doc/杂项必看.md
97
doc/杂项必看.md
@@ -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(); // 对象自己处理状态变更
|
||||
```
|
||||
237
doc/样式隔离与共享的方案.md
Normal file
237
doc/样式隔离与共享的方案.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 微前端架构中样式隔离与共享的方案及最佳实践
|
||||
|
||||
在微前端架构中,样式隔离与共享需要兼顾 "模块间样式不冲突" 和 "公共样式高效复用",可以结合以下方案实现:
|
||||
|
||||
### **一、样式隔离方案**
|
||||
|
||||
确保各微应用的样式不会相互污染,推荐 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 生成)
|
||||
Reference in New Issue
Block a user