@fulgurjs/federation 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/DESIGN.md +190 -0
- package/LICENSE +21 -0
- package/README.md +513 -0
- package/dist/cli.js +650 -0
- package/dist/config.cjs +114 -0
- package/dist/config.d.cts +79 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.js +77 -0
- package/dist/index.cjs +1698 -0
- package/dist/index.d.cts +82 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +1663 -0
- package/dist/pages.cjs +169 -0
- package/dist/pages.d.cts +66 -0
- package/dist/pages.d.ts +66 -0
- package/dist/pages.js +143 -0
- package/dist/runtime.js +3 -0
- package/docs/P5-vite7-8/345/205/274/345/256/271/347/237/251/351/230/265.md +36 -0
- package/docs/manual.html +467 -0
- package/docs/vite-upstream-issue-irregexp.md +91 -0
- package/docs/webpack-mf-/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +37 -0
- package/docs//346/262/231/347/256/261/350/276/271/347/225/214/345/256/241/350/256/241.md +45 -0
- package/docs//350/277/201/347/247/273/346/214/207/345/215/227.md +80 -0
- package/examples/fulgur.config.example.ts +41 -0
- package/package.json +90 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 沙箱边界审计(CSS / 全局变量 / 公共依赖)
|
|
2
|
+
|
|
3
|
+
> 日期 2026-09-16 | 实测脚本 `e2e/sandbox-audit.mjs`、`e2e/sandbox-audit2.mjs` | 环境 dev(admin+bpm+lowcode 三应用联邦)
|
|
4
|
+
|
|
5
|
+
## 结论
|
|
6
|
+
|
|
7
|
+
**联邦没有沙箱,也不需要沙箱**——这是与 qiankun 的根本架构差异。qiankun 用 JS Proxy 假 window + 样式隔离做「隔离共存」;@fulgurjs/federation(与 webpack MF 一致)做「**同 realm 共存 + 依赖级隔离**」:所有联邦模块与宿主跑在同一个 window/同一个 CSSOM 里,靠**共享依赖单例协商**防止最大的互扰源(双 Vue 运行时),而不是靠沙箱隔开。
|
|
8
|
+
|
|
9
|
+
## 实测数据(admin 宿主 + bpm + lowcode 联邦加载前后对比)
|
|
10
|
+
|
|
11
|
+
### 1. 全局变量(window)
|
|
12
|
+
|
|
13
|
+
- 联邦加载前后 window 新增键**仅 17 个**,且全部可解释:
|
|
14
|
+
- `mainAppProps`/`mainRouter`:宿主→子应用的桥接对象(对齐乾坤 mainAppProps 语义,by design)
|
|
15
|
+
- `__VUE_I18N_*`/`__INTLIFY_*` 5 个:vue-i18n 的编译期 feature flags(库本身行为)
|
|
16
|
+
- `Prism`/`Sortable`:lowcode 设计器引入的第三方库全局
|
|
17
|
+
- `exports`/`fs`/`stream`/`util`/`chokidar` 等:lowcode 某设计器依赖把 Node 风格 CJS 包装漏到 window(**原版乾坤下同样存在**,属子应用依赖自身的脏行为,与联邦无关)
|
|
18
|
+
- 联邦运行时唯一注册面:`window.__FULGUR_SCOPE__`(值 `['default']`)与 `__FULGUR_INFO__`——无全局散落
|
|
19
|
+
- **未发现子应用覆盖宿主关键全局**
|
|
20
|
+
|
|
21
|
+
### 2. CSS
|
|
22
|
+
|
|
23
|
+
- 样式表数量:登录后基线 249 → 联邦加载后 470(+221,子应用页面样式按需注入,**expose chunk 的 CSS 自动加载**,fixtures B-17 用例覆盖)
|
|
24
|
+
- `:root --el-color-primary` 基线与加载后均为 `#409eff`——admin(element-plus 2.14.3)/bpm(2.9.1)/lowcode(2.10.2) **三版本 element-plus CSS 共存**,主色变量未被破坏
|
|
25
|
+
- 风格主战场:Vue SFC scoped 样式(data-v hash)天然隔离;组件库 CSS 类名前缀(.el-/.avue-/.vxe-)天然分区
|
|
26
|
+
- **已知理论边界**:多版本组件库 CSS 同挂 `:root` 变量,后加载覆盖先加载——当前三版本变量值一致故无实际影响;若未来某版本改了变量默认值,以「最后加载为准」,这是同 realm CSSOM 的固有权衡(webpack MF 同样如此)
|
|
27
|
+
|
|
28
|
+
### 3. 公共方法/依赖实例
|
|
29
|
+
|
|
30
|
+
| 类别 | 机制 | 实测证据 |
|
|
31
|
+
|------|------|---------|
|
|
32
|
+
| **shared 键**(vue/pinia/vue-router) | loadShare 协商单例,"已加载优先" | 联邦页面组件与宿主同为一份 Vue(双 Vue 曾致 'ce' 崩溃,修复后全站 0 报错即为单例实证);pinia/vue-router singleton 冲突警告显示协商发生且取宿主实例 |
|
|
33
|
+
| **非 shared 依赖**(lodash-es、axios、avue…) | 各子应用自带副本,模块作用域隔离 | 互不干扰;缺点是体积冗余(联邦的固有权衡) |
|
|
34
|
+
| **子应用全局注册**(全局组件/指令/字典) | federatedBoot **显式**注册进宿主 app 实例,失败显式报错(H3) | lowcode 组件数 335→686 的注册面清晰可见 |
|
|
35
|
+
|
|
36
|
+
## 三、给你的心智模型
|
|
37
|
+
|
|
38
|
+
| 维度 | qiankun | @fulgurjs/federation |
|
|
39
|
+
|------|---------|--------------------|
|
|
40
|
+
| JS 运行环境 | Proxy 假 window 沙箱 | **同一 window,无沙箱** |
|
|
41
|
+
| CSS | 可选严格隔离/重写 scoped | **同一 CSSOM,无隔离**(靠 scoped 样式+类名前缀+依赖版本对齐) |
|
|
42
|
+
| 公共依赖 | 子应用各自打包(或 externals) | **shared 单例协商**(更强:运行时版本协商+双版本共存) |
|
|
43
|
+
| 适用代价 | 隔离带来适配成本(全局桥、样式穿透、性能损耗) | 无沙箱要求依赖收敛(本案例已全量验证) |
|
|
44
|
+
|
|
45
|
+
**一句话**:联邦不做"防你犯错的隔离",做"让你们共享一切的编排"。全局变量要按团队约定加前缀;全局样式要 scoped 或带业务前缀;这三条纪律在 testbed 真实工程里已全部落实并实测无互扰。
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# 迁移指南:qiankun 微前端 → @fulgurjs/federation
|
|
2
|
+
|
|
3
|
+
> 本指南以 testbed/mes-zc(admin 宿主 + bpm/lowcode 子应用,21+6 页,真实工程)为案例。
|
|
4
|
+
|
|
5
|
+
## 一、概念映射
|
|
6
|
+
|
|
7
|
+
| qiankun | @fulgurjs/federation |
|
|
8
|
+
|---------|--------------------|
|
|
9
|
+
| 主应用 registerMicroApps | 宿主 `federation({ remotes })` |
|
|
10
|
+
| 子应用 entry(HTML) | remote entry(dev: `@fulgur-entry.js` 中间件 / prod: `fulgur-remoteEntry.js`) |
|
|
11
|
+
| 子应用生命周期 mount/unmount | 页面级 exposes(组件即入口,无生命周期样板) |
|
|
12
|
+
| 主子应用 window 隔离/沙箱 | 无沙箱:同 realm 直渲染(`window.xxx` 全局桥自然可用) |
|
|
13
|
+
| props 传递(qiankun 传参) | 组件 props(路由通道 = params+query 合并;弹窗通道 = 显式 v-bind) |
|
|
14
|
+
| 公共依赖 externals/主应用共享 | `shared`(singleton 协商,"已加载优先") |
|
|
15
|
+
| `qiankun` 运行时 + single-spa | `virtual:fulgur-runtime`(15KB 级,无 single-spa) |
|
|
16
|
+
|
|
17
|
+
## 二、迁移步骤(真实案例顺序)
|
|
18
|
+
|
|
19
|
+
1. **子应用装插件 + exposes**:把原菜单路由表逐页映射为 `exposes`(`./pages/bpm/task/todo` 对应路由 `bpm/task/todo`)。
|
|
20
|
+
2. **shared 声明**:vue/pinia/vue-router 设 singleton;**不要**把业务库(element-plus/avue/dayjs 等)放 shared——插件会在预构建与构建期自动处理其内部对 shared 键的引用(UMD/CJS-only 依赖支持)。
|
|
21
|
+
3. **宿主装插件 + remotes**:菜单 URL 通道由 `resolveFulgurPageFromPath` 按路由表解析 spec(见 `admin/src/qiankun/fulgurPages.ts`)。
|
|
22
|
+
4. **全局环境迁移**:子应用依赖宿主的全局(用户/字典 store、i18n、全局组件)→ 写 `federatedBoot.ts` expose,宿主加载页面前 `bootRemoteOnce` 执行;失败必须显式报错(H3)。
|
|
23
|
+
5. **路由参数**:页面组件用 `defineProps` 声明参数(id/taskId 等);宿主路由通道把 params+query 作为 props 传入。
|
|
24
|
+
6. **跨应用弹窗**:原 iframe 嵌子应用页 → `fulgurPageComponent(spec)` 直渲染 + props(见 FormDetailsModal);真外链(数据驱动 URL、AMIS 站点)保留 iframe。
|
|
25
|
+
7. **公共依赖坑**:
|
|
26
|
+
- 子应用 axios 的响应解包形状要和后端一致(jeecg `{success,result}` vs yudao `{code,data}`)
|
|
27
|
+
- 子应用全局注册(指令/组件)失败必须显式报错,禁止"忽略继续"
|
|
28
|
+
- 修改态表单禁用逻辑、页面门控(currentStep 等)保持原版语义
|
|
29
|
+
|
|
30
|
+
## 三、验收清单(从本案例提炼)
|
|
31
|
+
|
|
32
|
+
- [ ] 21+ 页双环境(dev 双 server / prod NGINX)路由级渲染一致
|
|
33
|
+
- [ ] 每页写操作闭环(新增/编辑/删除/发布/导出导入/审批)与原版逐项一致
|
|
34
|
+
- [ ] 控制台零报错(21 页 × 2 环境)
|
|
35
|
+
- [ ] 零 iframe(跨应用页面与弹窗;PDF/外链/数据驱动 URL 除外)
|
|
36
|
+
- [ ] 零兜底:联邦不可用显式报错(杀 remote 故障注入)
|
|
37
|
+
- [ ] 装后可用:npm pack → 干净项目 dev+prod(e2e/h7-install-test.sh)
|
|
38
|
+
|
|
39
|
+
## 三B、首次使用避坑清单(真实迁移踩坑实录,全文见仓库 README「避坑指南」节)
|
|
40
|
+
|
|
41
|
+
| # | 坑 | 症状 | 解法 |
|
|
42
|
+
|---|-----|------|------|
|
|
43
|
+
| 1 | 插件升级后未清 `.vite` | 页面渲染回旧逻辑 / 门面签名漂移 404 | `rm -rf node_modules/.vite` + 重启 dev + 换浏览器 profile |
|
|
44
|
+
| 2 | pnpm 装 tarball 软链断链 | `Cannot find module '@fulgurjs/federation'` | 重新 `pnpm add -D <tgz>` 并验证目录可达 |
|
|
45
|
+
| 3 | UMD/CJS 依赖被移出预构建 | dev 裸 CJS 白屏、`Cannot destructure property 'node'` | 放回 `optimizeDeps.include`(插件自动外部化 shared 键) |
|
|
46
|
+
| 4 | 给 shared 依赖加 ESM 别名 | 构建期 `xxx.default.extend is not a function` | **build 必删**(prod rollup 双重 interop,2026-09-16 二次定案);dev 侧若该依赖已移出预构建(如 element-plus),其 CJS 子路径需 dev 专用别名兜住(`command==='serve'` 才注入) |
|
|
47
|
+
| 5 | env 同步脚本回写 | 手改的 `.env.*` 被「还原」 | 改同步脚本的源头(如 `.env.backend`) |
|
|
48
|
+
| 6 | 项目构建自身 env 缺键 | `Cannot read properties of undefined (reading 'split')`(如 VITE_BUILD_COMPRESS) | 按项目构建要求补齐 env 键 |
|
|
49
|
+
| 7 | 登录异步链未完成就断言 | e2e 偶发被弹回登录页 | 等「登录表单消失」而非固定秒数;后台慢链 ≥15s |
|
|
50
|
+
| 8 | 多版本组件库 CSS | 后加载覆盖 `:root` 变量 | 主流版本变量一致则无感;升级时留意 |
|
|
51
|
+
| 9 | 后端缺端点 | 404/401 资源报错 | 代理/NGINX 层加诚实空响应垫片(不伪造业务数据) |
|
|
52
|
+
| 10 | 远程页面(exposes 目标文件)静态导入 `virtual:fulgur-runtime` | 宿主跨源加载后渲染上下文破坏:resolveComponent / withDirectives / "Missing ref owner" 告警、内容区静默空白 | 改走宿主单例(见下节);插件 dev 下已对此显式报错拦截 |
|
|
53
|
+
|
|
54
|
+
### 三B-1、远程页面如何取宿主运行时(跨源单例契约)
|
|
55
|
+
|
|
56
|
+
exposes 目标文件(被宿主跨源加载的远程页面/组件)**禁止静态 `import 'virtual:fulgur-runtime'`**——
|
|
57
|
+
该导入会改由远程 dev server 求值,在远程模块图内实例化第二份运行时副本,破坏渲染上下文
|
|
58
|
+
(插件 dev 下已对此显式报错,错误信息即指向本节)。
|
|
59
|
+
|
|
60
|
+
正确写法(二选一,取到的是同一份页面级单例):
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// ① 直接读全局单例(bpm detail 页先例)
|
|
64
|
+
const runtime = (globalThis as any).__FULGUR_RUNTIME__
|
|
65
|
+
const mod = await runtime.loadRemote('host-app/UserProfile')
|
|
66
|
+
|
|
67
|
+
// ② 经独立产物 getRuntime()(最小宿主/无构建场景,见 manual §5.1)
|
|
68
|
+
import { getRuntime, version } from '/fulgur-runtime.js'
|
|
69
|
+
const mod = await getRuntime().loadRemote('mes-bpm/./TaskCard')
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
运行时单例带 `version` 字段(与插件包版本同源)用于跨源副本一致性诊断;
|
|
73
|
+
宿主页面(非 exposes 目标)不受此限,可正常静态导入虚拟模块。
|
|
74
|
+
|
|
75
|
+
## 四、乾坤下线顺序
|
|
76
|
+
|
|
77
|
+
1. 联邦模式全量验收(本清单)
|
|
78
|
+
2. `QIANKUN_OPEN=false` 固化,保留一个版本周期回滚能力
|
|
79
|
+
3. 删除 qiankun 注册层(admin/src/qiankun/apps.ts 等)与 single-spa 依赖
|
|
80
|
+
4. 子应用可独立部署 ⇄ 宿主消费双形态长期保留
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用起步样例:一个宿主 + 一个远程(应用名/路径均为虚构)。
|
|
3
|
+
* 用法:npx fulgur init --config examples/fulgur.config.example.ts
|
|
4
|
+
* 完整字段说明见 `fulgur init` 生成的起步模板(fulgur init --template fulgur.config.ts)。
|
|
5
|
+
*/
|
|
6
|
+
import { defineFulgurConfig } from '@fulgurjs/federation/config'
|
|
7
|
+
|
|
8
|
+
export default defineFulgurConfig({
|
|
9
|
+
root: process.cwd(),
|
|
10
|
+
apps: [
|
|
11
|
+
{
|
|
12
|
+
path: 'apps/host',
|
|
13
|
+
name: 'host-app',
|
|
14
|
+
port: 5173,
|
|
15
|
+
base: '/',
|
|
16
|
+
host: {
|
|
17
|
+
remotePrefixes: { '/remote-a/': 'remote-a' },
|
|
18
|
+
remotes: {
|
|
19
|
+
'remote-a': { dev: 'http://localhost:5174/remote-a', prod: '/remote-a' },
|
|
20
|
+
},
|
|
21
|
+
pages: [
|
|
22
|
+
{ route: '/remote-a/home', name: 'RemoteAHome', title: '首页' },
|
|
23
|
+
{ route: '/remote-a/detail/:id', name: 'RemoteADetail', spec: 'pages/remote-a/detail', title: '详情' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: 'apps/remote-a',
|
|
29
|
+
name: 'remote-a',
|
|
30
|
+
port: 5174,
|
|
31
|
+
base: '/remote-a',
|
|
32
|
+
remote: {
|
|
33
|
+
exposes: {
|
|
34
|
+
'./pages/remote-a/home': './src/views/Home.vue',
|
|
35
|
+
'./pages/remote-a/detail': './src/views/Detail.vue',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
deploy: { webRoot: '/var/www/your-site', listen: 8080 },
|
|
41
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fulgurjs/federation",
|
|
3
|
+
"version": "0.4.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Vite Module Federation with full Webpack MF parity. Dev & production, no compromise.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./pages": {
|
|
17
|
+
"types": "./dist/pages.d.ts",
|
|
18
|
+
"import": "./dist/pages.js",
|
|
19
|
+
"require": "./dist/pages.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./config": {
|
|
22
|
+
"types": "./dist/config.d.ts",
|
|
23
|
+
"import": "./dist/config.js",
|
|
24
|
+
"require": "./dist/config.cjs"
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"CHANGELOG.md",
|
|
33
|
+
"DESIGN.md",
|
|
34
|
+
"examples",
|
|
35
|
+
"docs/manual.html",
|
|
36
|
+
"docs/迁移指南.md",
|
|
37
|
+
"docs/webpack-mf-对照与缺口.md",
|
|
38
|
+
"docs/沙箱边界审计.md",
|
|
39
|
+
"docs/P5-vite7-8兼容矩阵.md",
|
|
40
|
+
"docs/vite-upstream-issue-irregexp.md"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "cp ../../README.md README.md && mkdir -p docs && cp ../../docs/manual.html ../../docs/迁移指南.md ../../docs/webpack-mf-对照与缺口.md ../../docs/沙箱边界审计.md ../../docs/P5-vite7-8兼容矩阵.md ../../docs/vite-upstream-issue-irregexp.md docs/ && cp ../../CHANGELOG.md CHANGELOG.md && cp ../../DESIGN.md DESIGN.md && rm -rf examples && cp -r ../../examples examples && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js && node scripts/gen-runtime.mjs && tsup src/index.ts src/pages.ts src/config.ts --format esm,cjs --dts --external vite --target node18 --clean && esbuild src/cli.ts --bundle --format=esm --target=node18 --platform=node --outfile=dist/cli.js && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js",
|
|
44
|
+
"dev": "tsup --watch",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"typecheck": "tsc --noEmit"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"es-module-lexer": "^1.6.0",
|
|
50
|
+
"magic-string": "^0.30.17"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^24.0.0",
|
|
54
|
+
"tsup": "^8.4.0",
|
|
55
|
+
"typescript": "^5.8.0",
|
|
56
|
+
"vitest": "^3.1.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"vite": ">=5.0.0"
|
|
60
|
+
},
|
|
61
|
+
"pnpm": {
|
|
62
|
+
"onlyBuiltDependencies": [
|
|
63
|
+
"esbuild"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://github.com/chenmingye/fulgur-federation.git",
|
|
69
|
+
"directory": "packages/plugin"
|
|
70
|
+
},
|
|
71
|
+
"homepage": "https://github.com/chenmingye/fulgur-federation#readme",
|
|
72
|
+
"author": "chenmingye (Jason)",
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/chenmingye/fulgur-federation/issues"
|
|
75
|
+
},
|
|
76
|
+
"keywords": [
|
|
77
|
+
"vite",
|
|
78
|
+
"module-federation",
|
|
79
|
+
"federation",
|
|
80
|
+
"micro-frontend",
|
|
81
|
+
"vite-plugin",
|
|
82
|
+
"webpack-federation"
|
|
83
|
+
],
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">=18"
|
|
86
|
+
},
|
|
87
|
+
"bin": {
|
|
88
|
+
"fulgur": "./dist/cli.js"
|
|
89
|
+
}
|
|
90
|
+
}
|