Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,11 @@ src/projects/

### 🆕 如何新增一个新项目(推荐流程)

1) 新建目录:`src/projects/<yourProject>/`
2) 添加入口:`src/projects/<yourProject>/index.tsx`
3) (可选)添加路由覆盖:`src/projects/<yourProject>/routers/index.jsx`
4) 新增 pages/components 业务代码
5) 复制一份脚本(参考 `projectA/projectB`)或直接使用 `cross-env PROJECT=<yourProject> ...` 启动/构建
1. 新建目录:`src/projects/<yourProject>/`
2. 添加入口:`src/projects/<yourProject>/index.tsx`
3. (可选)添加路由覆盖:`src/projects/<yourProject>/routers/index.jsx`
4. 新增 pages/components 业务代码
5. 复制一份脚本(参考 `projectA/projectB`)或直接使用 `cross-env PROJECT=<yourProject> ...` 启动/构建

更多用法详见 [详细文档](./docs/README_PERMISSION.md) 与 [用户角色权限说明](./docs/USER_ROLE_PERMISSION.md)。

Expand All @@ -185,16 +185,16 @@ src/projects/

### ✅ 本地联调(3 个 devServer)

1) 启动两个 remote:
1. 启动两个 remote:

- `npm run start:mf:projectA`(默认 `http://localhost:8081/`,remoteEntry:`/remoteEntry.js`)
- `npm run start:mf:projectB`(默认 `http://localhost:8082/`,remoteEntry:`/remoteEntry.js`)

2) 启动 Shell(host):
2. 启动 Shell(host):

- `npm run start:mf:shell`(默认 `http://localhost:8080/`)

3) 访问:
3. 访问:

- `http://localhost:8080/#/portal`
- `http://localhost:8080/#/projectA`
Expand Down
35 changes: 27 additions & 8 deletions docs/MFE_DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ npx cross-env NODE_ENV=development MFE_ROLE=host webpack serve --config webpack/
### Remote 应用(远程项目)

启动 projectA:

```bash
# 推荐
npm run start:mf:projectA
Expand All @@ -74,6 +75,7 @@ npx cross-env NODE_ENV=development MFE_ROLE=remote PROJECT=projectA webpack serv
```

启动 projectB:

```bash
# 推荐
npm run start:mf:projectB
Expand Down Expand Up @@ -101,6 +103,7 @@ npx cross-env PROJECT=shell MFE_ROLE=host PUBLIC_URL=/ SENTRY_SOURCE_MAP=no BUIL
#### 构建远程应用(Remote)

构建 projectA:

```bash
# 推荐
npm run build:mf:projectA
Expand All @@ -112,6 +115,7 @@ npx cross-env PROJECT=projectA MFE_ROLE=remote PUBLIC_URL=/projectA/ SENTRY_SOUR
输出目录:`dist-projectA/`

构建 projectB:

```bash
# 推荐
npm run build:mf:projectB
Expand Down Expand Up @@ -254,7 +258,7 @@ services:
args:
MFE_ROLE: host
ports:
- "80:80"
- '80:80'
networks:
- mfe-network

Expand All @@ -266,7 +270,7 @@ services:
PROJECT_NAME: projectA
MFE_ROLE: remote
ports:
- "8081:80"
- '8081:80'
networks:
- mfe-network

Expand All @@ -278,7 +282,7 @@ services:
PROJECT_NAME: projectB
MFE_ROLE: remote
ports:
- "8082:80"
- '8082:80'
networks:
- mfe-network

Expand All @@ -288,6 +292,7 @@ networks:
```

启动所有服务:

```bash
docker-compose up -d
```
Expand Down Expand Up @@ -348,6 +353,7 @@ jobs:
#### 为什么推荐独立项目?

将多个 MFE 放在同一 Vercel 项目 + 子路径下,使用 `publicPath: 'auto'` 经常出问题:

- 刷新页面或深层路由时 `document.currentScript.src` 推断错误
- Vercel rewrites 与 Module Federation chunk 加载互相干扰
- 调试困难,排错成本高
Expand All @@ -370,15 +376,16 @@ jobs:

在 Vercel Dashboard 中创建三个独立项目,连接同一个 Git 仓库:

| 项目名 | 配置文件 | 用途 |
|--------|----------|------|
| `pro-react-admin-shell` | `vercel.shell.json` | Host 主应用 |
| `pro-react-admin-projecta` | `vercel.projectA.json` | Remote A |
| `pro-react-admin-projectb` | `vercel.projectB.json` | Remote B |
| 项目名 | 配置文件 | 用途 |
| -------------------------- | ---------------------- | ----------- |
| `pro-react-admin-shell` | `vercel.shell.json` | Host 主应用 |
| `pro-react-admin-projecta` | `vercel.projectA.json` | Remote A |
| `pro-react-admin-projectb` | `vercel.projectB.json` | Remote B |

#### 步骤 2: 配置各项目

**Shell (Host) 项目设置:**

- Framework Preset: `Other`
- Build Command: `npm run build:mf:shell`
- Output Directory: `dist-shell`
Expand All @@ -389,11 +396,13 @@ jobs:
```

**ProjectA (Remote) 项目设置:**

- Framework Preset: `Other`
- Build Command: `cross-env PROJECT=projectA MFE_ROLE=remote npm run build:mf`
- Output Directory: `dist-projectA`

**ProjectB (Remote) 项目设置:**

- Framework Preset: `Other`
- Build Command: `cross-env PROJECT=projectB MFE_ROLE=remote npm run build:mf`
- Output Directory: `dist-projectB`
Expand Down Expand Up @@ -422,6 +431,7 @@ projectb.yourdomain.com → ProjectB (Remote)
```

然后更新 Shell 的环境变量:

```
MFE_PROJECTA_URL=https://projecta.yourdomain.com/remoteEntry.js
MFE_PROJECTB_URL=https://projectb.yourdomain.com/remoteEntry.js
Expand All @@ -436,6 +446,7 @@ MFE_PROJECTB_URL=https://projectb.yourdomain.com/remoteEntry.js
- `vercel.projectB.json` - ProjectB (Remote) 配置

每个配置文件都包含:

- `buildCommand`: 构建命令
- `outputDirectory`: 输出目录
- `rewrites`: SPA 路由支持
Expand Down Expand Up @@ -489,13 +500,15 @@ VITE_API_BASE_URL=https://api.example.com
### 1. 检查文件结构

确保所有 `remoteEntry.js` 文件可访问:

- 主应用:`https://myapp.example.com/`
- projectA:`https://myapp.example.com/projectA/remoteEntry.js`
- projectB:`https://myapp.example.com/projectB/remoteEntry.js`

### 2. 浏览器控制台检查

打开主应用,查看控制台:

```javascript
// 应该能看到类似输出
[MFE] Remote projects configured: projectA, projectB
Expand All @@ -504,6 +517,7 @@ VITE_API_BASE_URL=https://api.example.com
### 3. 网络面板检查

检查是否成功加载了远程入口文件:

- `remoteEntry.js` 应该返回 200 状态
- 动态加载的 chunk 文件也应该成功加载

Expand All @@ -514,6 +528,7 @@ VITE_API_BASE_URL=https://api.example.com
**原因**: 路径配置不正确

**解决**:

- 检查 `webpack/mfe.config.js` 中的 `prodPath` 是否正确
- 检查 Nginx 配置是否正确映射路径
- 确认文件确实存在于目标目录
Expand All @@ -523,6 +538,7 @@ VITE_API_BASE_URL=https://api.example.com
**原因**: 跨域配置问题

**解决**:

- 如果同域部署,使用相对路径
- 如果跨域部署,配置正确的 CORS 头
- 使用完整的 HTTPS URL
Expand All @@ -532,6 +548,7 @@ VITE_API_BASE_URL=https://api.example.com
**原因**: 不同应用的依赖版本不一致

**解决**:

- 统一所有应用的 React、Antd 等关键依赖版本
- 检查 `package.json` 确保版本一致
- 使用 `npm list <package>` 检查实际安装版本
Expand All @@ -541,6 +558,7 @@ VITE_API_BASE_URL=https://api.example.com
**原因**: SPA 路由与静态文件服务冲突

**解决**:

- 使用 `try_files $uri $uri/ /index.html` 配置
- 确保 `publicPath` 设置正确(通常为 'auto' 或 '/')

Expand All @@ -562,6 +580,7 @@ VITE_API_BASE_URL=https://api.example.com
## 监控与日志

建议添加以下监控:

- 远程模块加载成功率
- 加载时间监控
- 错误日志收集
Expand Down
2 changes: 1 addition & 1 deletion src/projects/shell/public/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
Expand Down
30 changes: 15 additions & 15 deletions webpack/mfe.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,23 @@ export function parseRemotesFromEnv() {
if (!remotesEnv) return remoteProjects

try {
const parsed = remotesEnv.split(',').map((item) => {
const [name, url] = item.trim().split('@')
if (!name || !url) return null

return {
name: name.trim(),
devUrl: url.trim(),
prodPath: url.trim(),
}
}).filter(Boolean)
const parsed = remotesEnv
.split(',')
.map((item) => {
const [name, url] = item.trim().split('@')
if (!name || !url) return null

return {
name: name.trim(),
devUrl: url.trim(),
prodPath: url.trim(),
}
})
.filter(Boolean)

// 合并配置:环境变量优先
const envNames = new Set(parsed.map(p => p.name))
const result = [
...parsed,
...remoteProjects.filter(p => !envNames.has(p.name))
]
const envNames = new Set(parsed.map((p) => p.name))
const result = [...parsed, ...remoteProjects.filter((p) => !envNames.has(p.name))]

return result
} catch (error) {
Expand Down
6 changes: 3 additions & 3 deletions webpack/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,9 @@ const config = {
'@utils': path.resolve('./src/utils'),
'@theme': path.resolve('./src/theme'),
// 确保关键依赖只有一个实例,避免 zustand middleware 错误
'zustand': path.resolve('./node_modules/zustand'),
'immer': path.resolve('./node_modules/immer'),
'react': path.resolve('./node_modules/react'),
zustand: path.resolve('./node_modules/zustand'),
immer: path.resolve('./node_modules/immer'),
react: path.resolve('./node_modules/react'),
'react-dom': path.resolve('./node_modules/react-dom'),
},
symlinks: false,
Expand Down
Loading