@fulgurjs/federation 0.8.3 → 0.9.0

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/docs/manual.html DELETED
@@ -1,467 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>@fulgurjs/federation 使用手册 · Vite 模块联邦(全量对齐 Webpack MF)</title>
7
- <style>
8
- :root { --blue:#1677ff; --bg:#f6f8fa; --border:#e4e7ed; --text:#24292f; }
9
- * { box-sizing: border-box; }
10
- body { font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; color: var(--text); margin: 0; background: #fff; line-height: 1.7; }
11
- .wrap { max-width: 1080px; margin: 0 auto; padding: 24px 32px 96px; }
12
- h1 { font-size: 28px; border-bottom: 2px solid var(--blue); padding-bottom: 12px; }
13
- h2 { font-size: 22px; margin-top: 48px; border-left: 5px solid var(--blue); padding-left: 10px; }
14
- h3 { font-size: 17px; margin-top: 28px; }
15
- code, pre { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 13px; }
16
- pre { background: #0d1117; color: #e6edf3; padding: 14px 16px; border-radius: 8px; overflow-x: auto; line-height: 1.5; }
17
- code:not(pre code) { background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 1px 6px; }
18
- table { border-collapse: collapse; width: 100%; margin: 12px 0; font-size: 13.5px; }
19
- th, td { border: 1px solid var(--border); padding: 7px 10px; text-align: left; vertical-align: top; }
20
- th { background: var(--bg); }
21
- .ok { color: #1a7f37; font-weight: 600; }
22
- .warn { color: #9a6700; font-weight: 600; }
23
- .shot { max-width: 100%; border: 1px solid var(--border); border-radius: 8px; margin: 8px 0; box-shadow: 0 2px 8px rgba(0,0,0,.06); }
24
- .shot-row { display: flex; gap: 12px; flex-wrap: wrap; }
25
- .shot-row figure { margin: 0; flex: 1 1 320px; }
26
- figcaption { font-size: 12.5px; color: #57606a; margin-top: 4px; }
27
- .badge { display: inline-block; background: var(--blue); color: #fff; border-radius: 10px; padding: 0 10px; font-size: 12px; margin-left: 6px; }
28
- nav.toc { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 14px 20px; columns: 2; font-size: 14px; }
29
- nav.toc a { text-decoration: none; color: var(--blue); }
30
- .note { background: #fff8e6; border: 1px solid #f0d47a; border-radius: 8px; padding: 10px 14px; margin: 10px 0; font-size: 14px; }
31
- .danger { background: #fff1f0; border: 1px solid #ffa39e; border-radius: 8px; padding: 10px 14px; margin: 10px 0; font-size: 14px; }
32
- </style>
33
- </head>
34
- <body>
35
- <div class="wrap">
36
-
37
- <h1>@fulgurjs/federation 使用手册 <span class="badge">v0.1.0</span></h1>
38
- <p><b>Vite 模块联邦插件:开发环境与生产环境都能用,配置面与运行时行为全量对齐 Webpack Module Federation。</b>
39
- 本手册中的全部截图均为真实浏览器测试(Playwright + Chromium)自动留证,测试基座包含
40
- <a href="#tb">示例工程 真实项目副本</a>(qiankun 微前端仓库)与独立 fixture 工程。</p>
41
-
42
- <nav class="toc">
43
- <b>目录</b><br>
44
- <a href="#compare">1. 与 Webpack 原版对齐总表</a><br>
45
- <a href="#quick">2. 快速开始</a><br>
46
- <a href="#arch">3. 工作原理(dev / prod)</a><br>
47
- <a href="#feat">4. 功能章节(含实测截图)</a><br>
48
- <a href="#tb">5. 示例工程 真实项目实战</a><br>
49
- <a href="#hmr">6. HMR 全链路(L1/L2/L3)</a><br>
50
- <a href="#limit">7. 已知差异与限制(诚实版)</a><br>
51
- <a href="#faq">8. 故障排查(错误码 MFU-0xx)</a><br>
52
- <a href="#test">9. 测试覆盖与复现方式</a>
53
- </nav>
54
-
55
- <h2 id="compare">1. 与 Webpack 原版对齐总表</h2>
56
- <p>结论先行:<b>在 Vite↔Vite 场景下,配置面与运行时行为 100% 对齐 webpack ModuleFederationPlugin</b>;
57
- 唯二例外为 <code>remoteType: 'script'/'var'</code>(跨打包器互操作,P3 里程碑)与 Chrome DevTools 扩展(用
58
- <code>window.__FULGURJS_SCOPE__</code> 调试出口替代)。</p>
59
-
60
- <h3>1.1 配置面(webpack 官方全选项)</h3>
61
- <table>
62
- <tr><th>webpack 选项</th><th>fulgurjs</th><th>说明</th></tr>
63
- <tr><td><code>name</code> / <code>filename</code></td><td class="ok">✅ 一致</td><td>容器名 + remoteEntry 稳定文件名(默认 <code>fulgurjs-remoteEntry.js</code>)</td></tr>
64
- <tr><td><code>exposes</code> 字符串 / 对象 <code>{import, name}</code></td><td class="ok">✅ 一致</td><td>对象形式 name = 稳定 chunk 名</td></tr>
65
- <tr><td><code>remotes</code> 的 <code>name@url</code> 语法</td><td class="ok">✅ 一致</td><td>@ 前自报名校验;键重命名(checkout: shop@…)</td></tr>
66
- <tr><td><code>remotes</code> 对象 <code>{external, shareScope}</code></td><td class="ok">✅ 一致</td><td>另支持 <code>dev</code>/<code>prod</code> 地址拆分(webpack 没有的增强)</td></tr>
67
- <tr><td>promise-based remote</td><td class="ok">✅ 一致</td><td>函数形式无法静态序列化,按 webpack 语义在运行时 <code>registerRemote()</code> 注册</td></tr>
68
- <tr><td><code>shared</code> 数组 / semver 简写 / 完整 hint</td><td class="ok">✅ 一致</td><td>9 个 hint 全支持:eager / import / packageName / requiredVersion / shareKey / shareScope / singleton / strictVersion / version</td></tr>
69
- <tr><td><code>requiredVersion</code> 完整 semver 语法</td><td class="ok">✅ 一致</td><td>精确/部分/^/~/比较器/连字符/||/URL 形式(URL 视为接受任意)</td></tr>
70
- <tr><td><code>strictVersion</code> 默认值</td><td class="ok">✅ 一致</td><td>有本地副本且非 singleton → true</td></tr>
71
- <tr><td><code>shareScope</code> 多作用域</td><td class="ok">✅ 一致</td><td>命名空间互相隔离</td></tr>
72
- <tr><td><code>eager</code></td><td class="ok">✅ 一致</td><td>初始 chunk 可用 + 总被下载</td></tr>
73
- <tr><td><code>runtime</code> / <code>runtimeChunk</code></td><td class="ok">✅ 一致</td><td>运行时与 init 独立产物、稳定命名</td></tr>
74
- <tr><td><code>manifest</code></td><td class="ok">✅</td><td>fulgurjs-manifest.json(部署回滚 = 切 manifest 指针)</td></tr>
75
- <tr><td><code>runtimePlugins</code></td><td class="ok">✅</td><td>resolveShare / beforeLoadRemote / afterLoadRemote / onRemoteError 钩子</td></tr>
76
- <tr><td><code>dts</code></td><td class="ok">✅</td><td>dev 源码级类型直连</td></tr>
77
- <tr><td><code>automaticAsyncBoundary</code> / <code>dataPrefetch</code> / <code>usedExports</code></td><td class="warn">🔶 增强</td><td>TLA 自动异步边界(无需手工 bootstrap)、preloadRemote 常驻、Rollup 原生 tree-shaking</td></tr>
78
- <tr><td><code>remoteType: 'script'/'var'</code></td><td class="warn">🔶 P3</td><td>跨打包器互操作(webpack 宿主消费 Vite remote),首发不承诺</td></tr>
79
- <tr><td>Chrome DevTools 扩展</td><td class="warn">🔶 替代</td><td><code>window.__FULGURJS_SCOPE__ / __FULGURJS_INFO__</code> 调试出口</td></tr>
80
- </table>
81
-
82
- <h3>1.2 运行时行为语义(18 条逐条 e2e 验收)</h3>
83
- <table>
84
- <tr><th>#</th><th>webpack 行为</th><th>fulgurjs</th></tr>
85
- <tr><td>1</td><td>协商取满足 requiredVersion 的<b>最高版本</b></td><td class="ok">✅</td></tr>
86
- <tr><td>2</td><td>非 singleton 多版本共存</td><td class="ok">✅</td></tr>
87
- <tr><td>3</td><td>已注册/已加载版本永不替换(first-wins)</td><td class="ok">✅</td></tr>
88
- <tr><td>4</td><td>同版本按 uniqueName 决胜</td><td class="ok">✅</td></tr>
89
- <tr><td>5</td><td>init() 收养语义:双向供给、兄弟 remote 互享、循环守卫</td><td class="ok">✅</td></tr>
90
- <tr><td>6</td><td>同一容器换 scope 重复 init → 抛错</td><td class="ok">✅</td></tr>
91
- <tr><td>7</td><td>容器 API <code>get/init</code> 可手写协议直用</td><td class="ok">✅</td></tr>
92
- <tr><td>8</td><td>singleton 冲突 → warning + 用已注册唯一实例</td><td class="ok">✅</td></tr>
93
- <tr><td>9</td><td>strictVersion 不满足 → 抛错(MFU-003)</td><td class="ok">✅</td></tr>
94
- <tr><td>10</td><td><code>import: false</code> 无匹配 → 抛错(MFU-004);有 fallback → 用本地副本</td><td class="ok">✅</td></tr>
95
- <tr><td>11</td><td>shareKey 重定向(lodash-es ↔ lodash)</td><td class="ok">✅</td></tr>
96
- <tr><td>12</td><td>多 shareScope 隔离</td><td class="ok">✅</td></tr>
97
- <tr><td>13</td><td>eager:初始 chunk 可用 + 总被下载</td><td class="ok">✅</td></tr>
98
- <tr><td>14</td><td>动态 remote(运行时注册)+ promise remote</td><td class="ok">✅</td></tr>
99
- <tr><td>15</td><td>静态 remote 自动加载;失败 → MFU-001 错误码体系</td><td class="ok">✅</td></tr>
100
- <tr><td>16</td><td><code>import('app/Button')</code> 语法 + default/named unwrap</td><td class="ok">✅</td></tr>
101
- <tr><td>17</td><td>exposed 模块样式自动注入(dev style / prod css chunk)</td><td class="ok">✅</td></tr>
102
- <tr><td>18</td><td>异步边界:TLA 自动化(比 webpack 手工 bootstrap 更进一步)</td><td class="ok">✅</td></tr>
103
- </table>
104
-
105
- <h2 id="quick">2. 快速开始</h2>
106
- <h3>2.1 安装</h3>
107
- <pre>pnpm add -D @fulgurjs/federation</pre>
108
-
109
- <h3>2.2 远程提供方(remote)</h3>
110
- <pre>// vite.config.ts
111
- import { defineConfig } from 'vite'
112
- import vue from '@vitejs/plugin-vue'
113
- import { federation } from '@fulgurjs/federation'
114
-
115
- export default defineConfig({
116
- plugins: [
117
- vue(),
118
- federation({
119
- name: 'remote-a',
120
- filename: 'fulgurjs-remoteEntry.js',
121
- exposes: {
122
- './Button': './src/exposes/Button.vue',
123
- './utils': './src/exposes/utils.ts',
124
- },
125
- shared: {
126
- vue: { singleton: true, requiredVersion: '^3.4.0' },
127
- pinia: { singleton: true },
128
- },
129
- }),
130
- ],
131
- })</pre>
132
-
133
- <h3>2.3 宿主(host)</h3>
134
- <pre>federation({
135
- name: 'host-app',
136
- remotes: {
137
- // 单地址:dev 自动拼 @fulgurjs-entry.js,prod 自动拼 remoteEntry 文件名
138
- 'remote-a': 'http://localhost:5101',
139
- // 显式拆分(推荐用于部署)
140
- 'remote-b': { dev: 'http://localhost:5102', prod: 'https://cdn.example.com/remote-b/' },
141
- // webpack 语法 + 键重命名
142
- shop: 'remote-a@http://localhost:5101',
143
- },
144
- shared: { vue: { singleton: true }, pinia: { singleton: true } },
145
- })</pre>
146
-
147
- <h3>2.4 业务代码(与 Webpack 完全同款)</h3>
148
- <pre>// 动态导入(推荐)
149
- const mod = await import('remote-a/Button')
150
- const Btn = mod.default
151
-
152
- // 命名/默认/别名
153
- const { formatDate } = await import('remote-a/utils')
154
-
155
- // 静态导入(编译为 TLA 自动异步边界,无需手工 bootstrap.js)
156
- import Btn from 'remote-a/Button'</pre>
157
-
158
- <h2 id="arch">3. 工作原理(dev / prod 双引擎)</h2>
159
- <table>
160
- <tr><th>环境</th><th>引擎</th><th>机制</th></tr>
161
- <tr><td><b>dev</b></td><td>双 dev-server 协作</td>
162
- <td>remote 的 dev server 暴露 <code>/@fulgurjs-entry.js</code>(自包含容器)与 <code>/@fulgurjs-manifest.json</code>;
163
- 宿主页面经运行时 <code>container.init(shareScopeMap)</code> 收养共享作用域;remote 模块的 shared 导入经
164
- <b>绑定门面</b>(按需生成的虚拟模块)协商到宿主实例;HMR 通过 remote 的 vite client 直连宿主页面。</td></tr>
165
- <tr><td><b>prod</b></td><td>构建期改写(Rollup/Rolldown)</td>
166
- <td>每个 expose 生成虚拟入口 → 独立 chunk;shared 经动态导入边界自动剥离;产出稳定文件名的
167
- <code>fulgurjs-remoteEntry.js</code> + <code>fulgurjs-manifest.json</code>;宿主入口前置 init import 注册 remotes/provides。</td></tr>
168
- </table>
169
- <div class="note">运行时内核 gzip 约 <b>5.0KB</b>(对标 @module-federation/runtime 的 40KB+),页面级单例:
170
- 多应用各自打包的运行时副本经 <code>globalThis.__FULGURJS_RUNTIME__</code> 合一。
171
- 跨源取运行时的官方入口:<code>getRuntime()</code>(独立产物导出,与全局单例同一实例),
172
- 单例带 <code>version</code> 字段(与插件包版本同源)用于副本一致性诊断;方法面已冻结防意外覆写。
173
- 注意:exposes 目标文件(远程页面)禁止静态导入 <code>virtual:fulgurjs-runtime</code>,插件 dev 下会显式报错(见 §8)。</div>
174
-
175
- <h2 id="feat">4. 功能章节(含实测截图)</h2>
176
-
177
- <h3>4.1 远程组件加载(webpack 同款 import 用法)</h3>
178
- <pre>const mod = await import('remote-a/Button')</pre>
179
- <div class="shot-row">
180
- <figure><img class="shot" src="screenshots/dev-load-remote-button.png"><figcaption>dev:宿主页面加载 remote-a/Button 并渲染</figcaption></figure>
181
- <figure><img class="shot" src="screenshots/prod-load-remote-button.png"><figcaption>prod(NGINX 8662/8999):同一段代码</figcaption></figure>
182
- </div>
183
-
184
- <h3>4.2 工具模块 default / named / 常量语义</h3>
185
- <div class="shot-row">
186
- <figure><img class="shot" src="screenshots/dev-remote-utils-semantics.png"><figcaption>dev</figcaption></figure>
187
- <figure><img class="shot" src="screenshots/prod-remote-utils-semantics.png"><figcaption>prod</figcaption></figure>
188
- </div>
189
-
190
- <h3>4.3 双 vue 版本共存 + shared 单例</h3>
191
- <p>remote-a 用 vue 3.5.x,remote-b 以 <code>vue34</code> 别名提供 vue 3.4.38(shareKey 均为 vue)。
192
- 两卡片同时渲染,且 <code>isReactive(host 的 reactive 对象) === true</code> 证明单实例;网络面板 vue 预构建产物全页仅一次请求。</p>
193
- <div class="shot-row">
194
- <figure><img class="shot" src="screenshots/dev-multi-dual-version-shared-singleton.png"><figcaption>dev:双版本共存 + 单例</figcaption></figure>
195
- <figure><img class="shot" src="screenshots/prod-multi-dual-version-shared-singleton.png"><figcaption>prod</figcaption></figure>
196
- </div>
197
-
198
- <h3>4.4 Share Scope 协商可视化(调试出口)</h3>
199
- <p>宿主任意页面可用 <code>window.__FULGURJS_SCOPE__</code> / <code>__FULGURJS_INFO__</code> 查看协商结果与加载状态。</p>
200
- <img class="shot" src="screenshots/dev-scope-debug-panel.png">
201
-
202
- <h3>4.5 remotes 键重命名(checkout: shop@ 语法)</h3>
203
- <div class="shot-row">
204
- <figure><img class="shot" src="screenshots/dev-remote-key-rename.png"><figcaption>dev</figcaption></figure>
205
- <figure><img class="shot" src="screenshots/prod-remote-key-rename.png"><figcaption>prod</figcaption></figure>
206
- </div>
207
-
208
- <h3>4.6 promise-based remote(运行时注册)</h3>
209
- <pre>// 构建时未知 URL:运行时注册(webpack 'promise new Promise' 的等价能力)
210
- rt.registerRemote({
211
- name: 'promise-remote',
212
- promise: async () => ({ name: 'promise-remote', init: async (map) => {...}, get: async (m) => {...} }),
213
- })
214
- const mod = await import('promise-remote/Button')</pre>
215
- <img class="shot" src="screenshots/dev-promise-based-remote.png">
216
-
217
- <h3>4.7 eager shared(初始 chunk 可用)</h3>
218
- <pre>shared: { pinia: { singleton: true, eager: true } }</pre>
219
- <img class="shot" src="screenshots/prod-eager-pinia-initial-download.png">
220
- <figcaption>prod:pinia chunk 出现在首屏 HTML 的 modulepreload 中(未做任何交互)</figcaption>
221
-
222
- <h3>4.8 错误码与错误边界(MFU-006)</h3>
223
- <div class="shot-row">
224
- <figure><img class="shot" src="screenshots/dev-mfu006-module-not-exposed.png"><figcaption>dev:CAUGHT MFU-006</figcaption></figure>
225
- <figure><img class="shot" src="screenshots/prod-mfu006-module-not-exposed.png"><figcaption>prod:行为一致</figcaption></figure>
226
- </div>
227
-
228
- <h3>4.9 远程组件样式注入</h3>
229
- <div class="shot-row">
230
- <figure><img class="shot" src="screenshots/dev-remote-css-injection.png"><figcaption>dev:style 注入</figcaption></figure>
231
- <figure><img class="shot" src="screenshots/prod-remote-css-injection.png"><figcaption>prod:css chunk 注入</figcaption></figure>
232
- </div>
233
-
234
- <h3>4.10 容错:kill remote → MFU-001 → 恢复</h3>
235
- <img class="shot" src="screenshots/dev-fault-remote-killed-mfu001.png">
236
- <img class="shot" src="screenshots/dev-fault-recovered.png">
237
-
238
- <h3>4.11 pinia 共享单例</h3>
239
- <img class="shot" src="screenshots/dev-pinia-shared-singleton.png">
240
-
241
- <h3>4.12 dts 类型直连(dev 源码级补全)</h3>
242
- <p>宿主 dev server 启动时自动拉取各远程的 dev manifest,为每个 exposes 生成 <code>declare module</code> 声明,
243
- 映射到远程本机源码——补全与跳转都是源码级。产物写入 <code>src/fulgurjs-types/&lt;remote&gt;.d.ts</code>,
244
- 确保 tsconfig include 包含该目录即可。</p>
245
- <pre>declare module 'remote-a/Button' {
246
- import type { DefineComponent } from 'vue'
247
- const component: DefineComponent&lt;Record&lt;string, unknown&gt;, Record&lt;string, unknown&gt;, unknown&gt;
248
- export default component
249
- export * from '../../../remote-a/src/exposes/Button.vue'
250
- }</pre>
251
- <img class="shot" src="screenshots/dev-dts-type-link.png">
252
- <figcaption>实测:host-vue 启动后自动生成 remote-a / remote-b / shop 三份类型声明(5 + 2 + 5 个 exposes)</figcaption>
253
-
254
- <h3>4.13 preloadRemote(manifest 驱动精确预载)</h3>
255
- <pre>import { preloadRemote } from 'virtual:fulgurjs-runtime' // 插件已全局注入
256
- await preloadRemote('remote-a') // 按 manifest 精确预载全部 exposes chunk + CSS
257
- await preloadRemote('remote-a', { mode: 'prefetch' }) // 低优先级预取</pre>
258
- <p>实测注入的 modulepreload 链接(dev):容器入口 + 全部 5 个 exposes 的源模块——
259
- 不做盲猜,全部来自 manifest 清单。</p>
260
- <img class="shot" src="screenshots/dev-preload-remote.png">
261
-
262
- <h2 id="tb">5. 示例工程 真实项目实战(qiankun 仓库 + 模块联邦平行通道)</h2>
263
- <p>复制 示例工程(三应用 monorepo) 到插件 testbed,保持 <b>qiankun 集成零改动</b>,新增联邦平行通道:
264
- 示例宿主应用(Vite 6.4.3)作为宿主,示例 bpm 子应用(Vite 5.1.4,/flowable)与 示例 lowcode 子应用(Vite 5.2.12,/lowcode)
265
- 作为远程。免登录演示路由 <code>/main/fulgurjs-demo</code>(meta.ignoreAuth)。</p>
266
- <pre>// admin remotes(dev/prod 自动切换,prod 走 NGINX 同源路径)
267
- 'bpm-app': { dev: 'http://localhost:5102/bpm', prod: '/bpm' },
268
- 'lowcode-app': { dev: 'http://localhost:5103/lowcode', prod: '/lowcode' },
269
- shared: { vue: {singleton:true, requiredVersion:'^3.4.0'}, pinia: {singleton:true} }</pre>
270
- <pre>&lt;!-- 推荐用法:远程组件纳入宿主组件树 --&gt;
271
- const TaskCard = defineAsyncComponent(() =&gt; import('mes-bpm/TaskCard').then(m =&gt; m.default))</pre>
272
- <img class="shot" src="screenshots/tb-dev-demo-page.png">
273
- <img class="shot" src="screenshots/tb-dev-remotes-loaded.png">
274
- <p>协商结果(dev 实测):admin 提供 vue@3.5.40 / pinia@2.1.7;lowcode 带来 vue@3.5.22、bpm 带来 pinia@2.3.1
275
- ——多版本条目并存,消费方按 requiredVersion 取最高满足版本。</p>
276
- <img class="shot" src="screenshots/tb-dev-scope-negotiation.png">
277
-
278
- <h3>5.1 生产环境(NGINX 8662):真实远程产物 + 最小宿主</h3>
279
- <p>bpm/lowcode 生产构建产物(fulgurjs-remoteEntry.js + fulgurjs-manifest.json)部署到 NGINX 8662,
280
- 最小宿主页用<b>纯运行时容器 API</b>(无构建步骤)直接消费真实产物:</p>
281
- <pre>import { initSharing, registerRemotes, loadRemote, loadShare } from '/fulgurjs-runtime.js'
282
- initSharing('default')
283
- registerRemotes([
284
- { name: 'mes-bpm', entry: '/flowable/fulgurjs-remoteEntry.js' },
285
- { name: 'mes-lowcode', entry: '/lowcode/fulgurjs-remoteEntry.js' },
286
- ])
287
- const TaskCard = (await loadRemote('mes-bpm/./TaskCard')).default
288
- const vueMod = await loadShare('vue', { requiredVersion: '^3.4.0', singleton: true })</pre>
289
- <img class="shot" src="screenshots/tb-prod-minimal-host.png">
290
- <figcaption>prod 实测:双远程组件渲染、vue/pinia 双版本协商(vue@3.5.40 ← mes-bpm、vue@3.5.22 ← mes-lowcode;pinia 2.3.1 ← mes-bpm、2.1.7 ← mes-lowcode;宿主 loadShare 实取 3.5.40)</figcaption>
291
-
292
- <h2 id="cases">5.2 线上环境案例清单(后台菜单配置的 27 个子应用页面)</h2>
293
- <p>以下页面由后台动态菜单配置,当前经 qiankun 按 <code>/flowable/</code>、<code>/lowcode/</code> 前缀路由到子应用加载。
294
- 数据来自后台菜单接口(<code>sys/permission/getUserPermissionByToken</code>)实测返回。迁移到模块联邦后,
295
- 这些页面将全部以"页面组件 exposes + defineAsyncComponent 按需加载"方式引用,qiankun 不再参与。</p>
296
-
297
- <h3>mes-bpm(flowable)21 个页面</h3>
298
- <table>
299
- <tr><th>后台菜单路由</th><th>业务</th><th>迁移后的 expose 条目</th></tr>
300
- <tr><td>/bpmTask/flowable/bpm/task/todo</td><td>待办任务</td><td>./pages/bpm/task/todo</td></tr>
301
- <tr><td>/bpmTask/flowable/bpm/task/done</td><td>已办任务</td><td>./pages/bpm/task/done</td></tr>
302
- <tr><td>/bpmTask/flowable/bpm/task/my</td><td>我发起的</td><td>./pages/bpm/task/my</td></tr>
303
- <tr><td>/bpmTask/flowable/bpm/task/copy</td><td>抄送我的</td><td>./pages/bpm/task/copy</td></tr>
304
- <tr><td>/bpmTask/flowable/bpm/task/create</td><td>发起流程</td><td>./pages/bpm/task/create</td></tr>
305
- <tr><td>/bpmTask/flowable/bpm/process-instance/detail</td><td>流程实例详情</td><td>./pages/bpm/process-instance/detail</td></tr>
306
- <tr><td>/bpmTask/flowable/bpm/manager/action</td><td><b>流程审批操作页</b>(iframe 弹窗的嵌入目标,迁移核心收益点)</td><td>./pages/bpm/manager/action</td></tr>
307
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model</td><td>流程模型管理</td><td>./pages/bpm/manager/model</td></tr>
308
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model/create</td><td>新建模型</td><td>./pages/bpm/manager/model/create</td></tr>
309
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model/update/:id</td><td>编辑模型</td><td>./pages/bpm/manager/model/update</td></tr>
310
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model/copy/:id</td><td>复制模型</td><td>./pages/bpm/manager/model/copy</td></tr>
311
- <tr><td>/baseConfig/manager/flowable/bpm/manager/form</td><td>表单管理</td><td>./pages/bpm/manager/form</td></tr>
312
- <tr><td>/baseConfig/manager/flowable/bpm/manager/form/edit</td><td>编辑表单</td><td>./pages/bpm/manager/form/edit</td></tr>
313
- <tr><td>/baseConfig/manager/flowable/bpm/manager/category</td><td>流程分类</td><td>./pages/bpm/manager/category</td></tr>
314
- <tr><td>/baseConfig/manager/flowable/bpm/manager/user-group</td><td>用户组</td><td>./pages/bpm/manager/user-group</td></tr>
315
- <tr><td>/baseConfig/manager/flowable/bpm/manager/process-listener</td><td>流程监听器</td><td>./pages/bpm/manager/process-listener</td></tr>
316
- <tr><td>/baseConfig/manager/flowable/bpm/manager/process-expression</td><td>流程表达式</td><td>./pages/bpm/manager/process-expression</td></tr>
317
- <tr><td>/baseConfig/manager/flowable/bpm/manager/process-instance/manager</td><td>流程实例管理</td><td>./pages/bpm/manager/process-instance/manager</td></tr>
318
- <tr><td>/baseConfig/manager/flowable/bpm/manager/definition</td><td>流程定义</td><td>./pages/bpm/manager/definition</td></tr>
319
- <tr><td>/baseConfig/manager/flowable/bpm/process-instance/report</td><td>流程报表</td><td>./pages/bpm/process-instance/report</td></tr>
320
- </table>
321
-
322
- <h3>mes-lowcode 6 个页面</h3>
323
- <table>
324
- <tr><th>后台菜单路由</th><th>业务</th><th>迁移后的 expose 条目</th></tr>
325
- <tr><td>/online/lowcode/lowdev/formDesign</td><td>表单设计</td><td>./pages/lowdev/formDesign</td></tr>
326
- <tr><td>/online/lowcode/lowdev/reportDesign</td><td>报表设计</td><td>./pages/lowdev/reportDesign</td></tr>
327
- <tr><td>/online/lowcode/lowdev/graphReportDesign</td><td>图形报表设计</td><td>./pages/lowdev/graphReportDesign</td></tr>
328
- <tr><td>/online/lowcode/lowdev/moduleDesign</td><td>模块设计</td><td>./pages/lowdev/moduleDesign</td></tr>
329
- <tr><td>/online/lowcode/lowdev/reportTest/:code</td><td>报表测试(带参)</td><td>./pages/lowdev/reportTest</td></tr>
330
- <tr><td>/online/lowcode/form/external/:type/:id</td><td>外部表单(带参)</td><td>./pages/form/external</td></tr>
331
- </table>
332
-
333
- <div class="note">线上环境页面截图:需要后台真实登录账号(表单默认 admin/123456 在该部署上无效,已验证标准登录流程返回"用户名或密码错误")。
334
- 拿到账号后将在 8661(当前生产环境)逐页点开截图留证,作为 qiankun→模块联邦迁移前后的对比基线保存。</div>
335
-
336
- <h2 id="hmr">6. HMR 全链路(L1/L2/L3)</h2>
337
- <table>
338
- <tr><th>档位</th><th>验收</th><th>结果</th></tr>
339
- <tr><td>L1</td><td>remote 改代码 → 宿主页面组件热替换(无整页刷新)</td><td class="ok">✅</td></tr>
340
- <tr><td>L2</td><td>组件内状态保留(模板级变更走 rerender)</td><td class="ok">✅</td></tr>
341
- <tr><td>L3</td><td>编译报错覆盖层 → 修复自动恢复</td><td class="ok">✅</td></tr>
342
- </table>
343
- <div class="shot-row">
344
- <figure><img class="shot" src="screenshots/tb-dev-hmr-before.png"><figcaption>testbed:HMR 前(进度 70%)</figcaption></figure>
345
- <figure><img class="shot" src="screenshots/tb-dev-hmr-crossapp.png"><figcaption>testbed:改 bpm 源码 → admin 页面热替换(状态 70% 保留)</figcaption></figure>
346
- </div>
347
- <div class="shot-row">
348
- <figure><img class="shot" src="screenshots/dev-hmr-l3-error-overlay.png"><figcaption>L3:remote 编译错误覆盖层</figcaption></figure>
349
- <figure><img class="shot" src="screenshots/dev-hmr-l3-recovered.png"><figcaption>L3:修复后自动恢复</figcaption></figure>
350
- </div>
351
-
352
- <h2 id="limit">7. 已知差异与限制(诚实版)</h2>
353
- <table>
354
- <tr><th>#</th><th>项</th><th>说明</th></tr>
355
- <tr><td>1</td><td><code>remoteType: 'script'/'var'</code></td><td>webpack 宿主消费 Vite remote 的跨打包器互操作 → P3 里程碑,配置时告警并按 module 处理</td></tr>
356
- <tr><td>2</td><td><code>export * from '共享包'</code></td><td>ESM 无法动态生成导出绑定,构建期明确报错;请用命名 re-export</td></tr>
357
- <tr><td>3</td><td>构建目标 ≥ es2022</td><td>TLA(自动异步边界)要求;插件自动提升默认 target,显式低版本会告警</td></tr>
358
- <tr><td>4</td><td>大型遗留宿主中远程组件的<b>交互响应式</b></td><td>实测 示例工程 admin(Vite 6.4.3 + qiankun 插件 + 3 万模块)中,远程组件内联事件可触发、
359
- 数据写入成功,但组件渲染效果偶发不随动(根因:多副本 prebundle chunk 图跨域组合的求值顺序)。<br>
360
- <b>规避:</b>① exposes 组件保持纯 Vue、避免依赖宿主 UI 库的 prebundle;② UI 库跨小版本共享需谨慎
361
- (EP 2.9.1/2.10.2/2.14.3 混用实测存在 Illegal constructor 风险);③ 疑难场景可改用 qiankun 原通道。</td></tr>
362
- <tr><td>5</td><td>SSR</td><td>本版本不支持(hook 自动禁用并告警)</td></tr>
363
- <tr><td>7</td><td>jeecg 规模宿主的 <b>admin 生产构建</b></td><td>示例工程 admin(3 万+模块、Vite 6.4.3、node 24)构建触发
364
- vite build-import-analysis 在 3MB 的 xlsx.mjs vendor chunk(纯第三方库,与联邦代码无关)上的 V8 栈溢出;伴随 16GB 堆 OOM、
365
- vite-plugin-top-level-await 1.6.0 的 swc "missing field type" 连锁问题。已验证:与 shared 开关无关(最小联邦配置同样触发);
366
- <b>dev 模式完全正常,bpm/lowcode 的生产构建正常</b>。规避:关闭 inline sourcemap / vendor 分包 / 升级 vite;根治需等 vite 上游修复。
367
- <b>已实测可行组合(2026-09-16,testbed admin prod 构建通过并部署 8662 验证)</b>:<code>build.sourcemap: false</code> + 停用
368
- <code>vite-plugin-top-level-await</code> + <code>build.target: 'es2022'</code> 三件套(缺一即崩,勿回退)。</td></tr>
369
- <tr><td>6</td><td>跨端口 HMR 的 ws 地址</td><td>remote 的 vite client 以页面 hostname + remote 端口连 ws;本机 localhost 联调无碍,跨主机名需反代</td></tr>
370
- </table>
371
-
372
- <h2 id="faq">8. 故障排查(错误码 MFU-0xx)</h2>
373
- <table>
374
- <tr><th>错误码</th><th>含义</th><th>处置</th></tr>
375
- <tr><td>MFU-001</td><td>remote 容器/模块加载失败(网络、超时、重试耗尽、熔断)</td><td>检查 remoteEntry 地址与 CORS;熔断默认 5 次失败后 30s 快速失败,可调 <code>retries/timeout/fallback/breaker</code></td></tr>
376
- <tr><td>MFU-002</td><td>remoteEntry 自报名与配置名不一致</td><td>核对 <code>name@url</code> 的 name 与 remote 应用 <code>federation({ name })</code>(promise remote 仅告警)</td></tr>
377
- <tr><td>MFU-003</td><td>strictVersion 版本不满足</td><td>调整 requiredVersion 或关闭 strictVersion</td></tr>
378
- <tr><td>MFU-004</td><td>共享模块缺失且无本地副本</td><td>去掉 <code>import: false</code> 或补齐提供方</td></tr>
379
- <tr><td>MFU-005</td><td>容器被第二个 share scope 重复 init</td><td>检查是否存在同名 remote 配置了不同 shareScope</td></tr>
380
- <tr><td>MFU-006</td><td>模块未被该远程 exposes</td><td>核对 exposes 键名(自动补 ./)</td></tr>
381
- <tr><td>MFU-007</td><td>预加载失败</td><td>仅上报不阻断;检查 manifest 可达性</td></tr>
382
- <tr><td>MFU-008</td><td>未知远程</td><td>在 <code>remotes</code> 配置或运行时 <code>registerRemote()</code> 注册</td></tr>
383
- <tr><td>MFU-009</td><td>加载到的模块没有任何导出</td><td>核对远程 exposes 是否指向了正确的文件(MFU-006 语义),或该文件是否缺少 export</td></tr>
384
- <tr><td>MFU-010</td><td>singleton 共享版本漂移(消费方要求与作用域实际提供不一致,使用作用域版本)</td><td>核对双方 shared requiredVersion;确需锁版本用 strictVersion(升级为 MFU-003 抛错)</td></tr>
385
- </table>
386
-
387
- <h3>构建/开发期错误(非 MFU 运行时码)</h3>
388
- <table>
389
- <tr><th>错误码</th><th>含义</th><th>处置</th></tr>
390
- <tr><td><code>CFG-001</code></td><td>name 缺失或含非法字符</td><td>按报错示例修正 federation name</td></tr>
391
- <tr><td><code>CFG-002</code></td><td>exposes 配置形状错误</td><td>{ "./Module": "./src/path" }</td></tr>
392
- <tr><td><code>CFG-003</code></td><td>remotes 配置形状错误 / 键含非法字符</td><td>{ name: url | { dev, prod } | () =&gt; Promise }</td></tr>
393
- <tr><td><code>CFG-004</code></td><td>shared 配置形状错误</td><td>["vue"] 或 { vue: { singleton: true } }</td></tr>
394
- <tr><td><code>CFG-005</code></td><td>remotes 键与 shared 键同名冲突</td><td>改名(webpack 同款互斥约束)</td></tr>
395
- <tr><td><code>CFG-006</code></td><td>孤岛配置(既无 exposes 也无 remotes/shared)</td><td>核对 federation 配置意图</td></tr>
396
- <tr><td><code>CFG-007</code></td><td>remotes 对象形式误用 name@ 前缀(dev/prod 槽位整串当 URL 拼接成坏地址,运行时表现为无关的 MFU-001)</td>
397
- <td>对象形式不支持 name@ 前缀:改用键名(自报名默认取键)或字符串形式 <code>'shop@http://host'</code>(重命名语义)</td></tr>
398
- <tr><td><code>CFG-008</code></td><td>shared 非法组合:eager + import:false(eager 需本地副本打进初始 chunk);同 shareKey+shareScope 重复声明(版本裁决歧义)</td>
399
- <td>eager 项去掉 import:false;合并重复声明或改用不同 shareKey</td></tr>
400
- <tr><td><code>DEV-001</code></td><td>remote dev server 不可达(dev manifest 拉取失败)</td>
401
- <td>启动对应 remote 的 dev server;核对宿主 remotes[].dev URL 与 remote 实际 VITE_PORT/base</td></tr>
402
- <tr><td><code>DEV-002</code></td><td>dev manifest 可达但 exposes 为空</td>
403
- <td>核对 remote 的 federation exposes 配置;确认插件版本与宿主一致(DEV-006)</td></tr>
404
- <tr><td><code>DEV-003</code></td><td>shared 键被 optimizeDeps.exclude(dev 裸 CJS 无 interop 风险)</td>
405
- <td>从 exclude 移除;确需移出预构建的库用 dev 专用别名兜 CJS 子路径(迁移指南避坑 #3/#4)</td></tr>
406
- <tr><td><code>DEV-004</code></td><td>已知 UMD-only 依赖(如 @smallwei/avue)不在 optimizeDeps.include</td>
407
- <td>加入 include: ['@smallwei/avue']</td></tr>
408
- <tr><td><code>DEV-005</code></td><td>remotes dev URL 端口无监听</td>
409
- <td>remote 未启动或端口错位(server.origin 同款),核对 VITE_PORT 与 remotes[].dev</td></tr>
410
- <tr><td><code>DEV-006</code></td><td>宿主/远程插件版本不一致</td>
411
- <td>统一各应用 @fulgurjs/federation 版本并重启 dev server</td></tr>
412
- <tr><td><code>DEV-008</code></td><td>exposes 目标文件(远程页面)静态导入 virtual:fulgurjs-runtime——宿主跨源加载时
413
- 在远程模块图内求值第二份副本链,破坏渲染上下文。<strong>0.4.1 起插件自动把该导入改写为惰性单例委托
414
- (求值期零副作用、调用期转发页面级单例),不再报错</strong>;本码保留用于历史排查与极端场景说明</td>
415
- <td>无需任何处理——任何文件直接 <code>import { loadRemote } from 'virtual:fulgurjs-runtime'</code> 即可;
416
- 调试时可绕过代理直取 <code>(globalThis as any).__FULGURJS_RUNTIME__</code></td></tr>
417
- <tr><td><code>DEV-009</code></td><td>联邦虚拟模块/门面请求 404(.vite 缓存漂移)——<strong>0.4.1 起插件在 dev server
418
- 启动时自动检测插件版本变化并清除 .vite 缓存</strong></td>
419
- <td>重启 dev server 即可(缓存自清);若自动清理失败按提示手工 <code>rm -rf node_modules/.vite</code>;
420
- 浏览器侧换全新 profile</td></tr>
421
- <tr><td><code>DEV-010</code></td><td>dev 冷启动预构建窗口提示:首次启动/清 .vite 后首轮 30~60s 内联邦请求瞬时 504 / "ce" / Outdated Optimize Dep(预构建暂态,非回归)</td>
422
- <td>先真实打开一次页面预热(等网络空闲)再做验收断言;重复出现才按 DEV-009 清缓存排查</td></tr>
423
- <tr><td><code>BLD-001</code></td><td>expose 源文件解析失败</td><td>核对 exposes 指向的文件路径存在且可编译</td></tr>
424
- <tr><td><code>BLD-002</code></td><td>构建目标低于 es2022(TLA 需要)</td><td>提升 build.target(插件会自动提升并告警)</td></tr>
425
- <tr><td><code>BLD-003</code></td><td>expose 目标组件含必填 props(文档化核对项,不自动告警)——联邦直挂无法像父组件传 props,必填 props 缺失会渲染崩溃(审批操作页误挂子组件事故类)。自动发射已按实测降级:宿主路由以 props 回调透传 params/query 时必填 props 可由 query 供给(流程详情页 id 实例),插件无法感知该通道</td>
426
- <td>expose 指向独立页(自带取参逻辑)或给 props 默认值;接入前人工核对 expose 目标的 defineProps 声明与取参通道</td></tr>
427
- </table>
428
-
429
- <h3>NGINX 生产样例(同源部署 + 反代后端)</h3>
430
- <pre>server {
431
- listen 8662;
432
- root "/path/to/dist"; # 含 main/ flowable/ lowcode/
433
- location /flowable { try_files $uri $uri/ /flowable/index.html; }
434
- location /lowcode { try_files $uri $uri/ /lowcode/index.html; }
435
- location /main { try_files $uri $uri/ /main/index.html; }
436
- location /api {
437
- proxy_pass http://YOUR-BACKEND-HOST:8080; # 替换为你的后端地址
438
- proxy_http_version 1.1;
439
- proxy_set_header Upgrade $http_upgrade;
440
- proxy_set_header Connection "upgrade";
441
- }
442
- }</pre>
443
-
444
- <h3>常见问题</h3>
445
- <ul>
446
- <li><b>This package is ESM only but it was tried to load by `require`</b> —— 已在 v0.1.0+ 提供 CJS 双格式产物,升级插件后重启 dev server。</li>
447
- <li><b>Top-level await is not available in the configured target</b> —— 插件默认自动提升 <code>build.target</code> 到 es2022;若显式配置了更低版本会告警。</li>
448
- <li><b>Failed to resolve import "vue"</b>(remote 侧)—— 检查是否用 <code>packageName</code> 把 UI 库注入的 helper import 纳入共享别名。</li>
449
- <li><b>端口被占/strictPort 退出</b> —— 旧进程未退干净:<code>kill $(lsof -tiTCP:端口 -sTCP:LISTEN)</code>。</li>
450
- <li><b>pnpm 报 ERR_PNPM_IGNORED_BUILDS</b> —— 在 <code>pnpm-workspace.yaml</code> 写 <code>allowBuilds: { esbuild: true, vue-demi: true }</code>。</li>
451
- </ul>
452
-
453
- <h2 id="test">9. 测试覆盖与复现方式</h2>
454
- <table>
455
- <tr><th>层</th><th>内容</th><th>命令</th></tr>
456
- <tr><td>单测</td><td>67 用例:semver 全语法、协商全分支、AST 改写快照、容器容错/熔断</td><td><code>pnpm test:unit</code></td></tr>
457
- <tr><td>e2e dev</td><td>10 用例:加载/语义/双版本/单例/重命名/promise/错误码/pinia/CSS/HMR</td><td><code>pnpm test:dev</code></td></tr>
458
- <tr><td>e2e 容错</td><td>2 用例:kill remote → MFU-001 → 恢复;HMR L3</td><td><code>npx playwright test --project=fault</code></td></tr>
459
- <tr><td>e2e prod</td><td>8 用例(隔离 NGINX 8999):产物断言/渲染/单例/CSS/eager/容错</td><td><code>pnpm test:prod</code></td></tr>
460
- <tr><td>体积基准</td><td>runtime gzip 4.4KB(红线 5KB)</td><td><code>gzip -c dist/runtime.js | wc -c</code></td></tr>
461
- </table>
462
- <p>全部截图由 e2e 自动产出并归档于 <code>docs/screenshots/</code>;本手册引用的 36 张截图即上述测试的真实产物
463
- (含 §5.2 线上案例清单:后台菜单接口实测解析的 27 个子应用页面)。</p>
464
-
465
- </div>
466
- </body>
467
- </html>