@fulgurjs/federation 5.1.4 → 5.2.1

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.
Files changed (84) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.en.md +5 -5
  3. package/README.md +12 -12
  4. package/dist/index.cjs +91 -3
  5. package/dist/index.js +91 -3
  6. package/dist/react-adapter.cjs +32 -1
  7. package/dist/react-adapter.d.cts +1 -1
  8. package/dist/react-adapter.d.ts +1 -1
  9. package/dist/react-adapter.js +32 -1
  10. package/dist/react.d.ts +1 -1
  11. package/dist/runtime-entry.d.ts +1 -1
  12. package/dist/runtime.js +1 -1
  13. package/dist/vue-adapter.cjs +99 -15
  14. package/dist/vue-adapter.d.cts +1 -1
  15. package/dist/vue-adapter.d.ts +1 -1
  16. package/dist/vue-adapter.js +100 -16
  17. package/examples/README.en.md +47 -0
  18. package/examples/README.md +40 -18
  19. package/examples/react/README.md +83 -0
  20. package/examples/react/host/README.md +49 -0
  21. package/examples/react/host/fulgurjs.config.ts +31 -0
  22. package/examples/{react-remote → react/host}/index.html +2 -1
  23. package/examples/react/host/package-lock.json +2383 -0
  24. package/examples/{react-host → react/host}/package.json +9 -8
  25. package/examples/react/host/src/App.tsx +19 -0
  26. package/examples/react/host/src/main.tsx +33 -0
  27. package/examples/react/host/src/pages/UtilsDemo.tsx +32 -0
  28. package/examples/react/host/src/remotePages.tsx +16 -0
  29. package/examples/{react-remote → react/host}/tsconfig.json +2 -3
  30. package/examples/{react-remote → react/host}/vite.config.ts +2 -2
  31. package/examples/react/remote/README.md +47 -0
  32. package/examples/react/remote/fulgurjs.config.ts +20 -0
  33. package/examples/{react-host → react/remote}/index.html +2 -1
  34. package/examples/react/remote/package-lock.json +2325 -0
  35. package/examples/{react-remote → react/remote}/package.json +8 -7
  36. package/examples/react/remote/src/App.tsx +16 -0
  37. package/examples/react/remote/src/exposes/ClickButton.tsx +15 -0
  38. package/examples/react/remote/src/exposes/pages/DetailPage.tsx +10 -0
  39. package/examples/react/remote/src/exposes/pages/HomePage.tsx +12 -0
  40. package/examples/react/remote/src/exposes/utils.ts +10 -0
  41. package/examples/react/remote/src/main.tsx +11 -0
  42. package/examples/{react-host → react/remote}/tsconfig.json +3 -4
  43. package/examples/{react-host → react/remote}/vite.config.ts +2 -2
  44. package/examples/vue/README.md +85 -0
  45. package/examples/vue/host/README.md +50 -0
  46. package/examples/vue/host/fulgurjs.config.ts +30 -0
  47. package/examples/vue/host/index.html +12 -0
  48. package/examples/vue/host/package-lock.json +1857 -0
  49. package/examples/vue/host/package.json +21 -0
  50. package/examples/vue/host/src/App.vue +12 -0
  51. package/examples/vue/host/src/main.ts +44 -0
  52. package/examples/vue/host/src/pages/HomePage.vue +16 -0
  53. package/examples/vue/host/src/pages/UtilsDemo.vue +45 -0
  54. package/examples/vue/host/tsconfig.json +13 -0
  55. package/examples/vue/host/vite.config.ts +8 -0
  56. package/examples/vue/remote/README.md +47 -0
  57. package/examples/vue/remote/fulgurjs.config.ts +20 -0
  58. package/examples/vue/remote/index.html +12 -0
  59. package/examples/vue/remote/package-lock.json +1835 -0
  60. package/examples/vue/remote/package.json +20 -0
  61. package/examples/vue/remote/src/App.vue +16 -0
  62. package/examples/vue/remote/src/exposes/ClickButton.vue +16 -0
  63. package/examples/vue/remote/src/exposes/pages/DetailPage.vue +14 -0
  64. package/examples/vue/remote/src/exposes/pages/HomePage.vue +11 -0
  65. package/examples/vue/remote/src/exposes/utils.ts +10 -0
  66. package/examples/vue/remote/src/main.ts +6 -0
  67. package/examples/vue/remote/tsconfig.json +13 -0
  68. package/examples/vue/remote/vite.config.ts +8 -0
  69. package/package.json +2 -2
  70. package/examples/host/fulgurjs.config.ts +0 -26
  71. package/examples/react-host/README.md +0 -28
  72. package/examples/react-host/fulgurjs.config.ts +0 -19
  73. package/examples/react-host/src/App.tsx +0 -66
  74. package/examples/react-host/src/federation/pages.data.ts +0 -21
  75. package/examples/react-host/src/main.tsx +0 -8
  76. package/examples/react-remote/fulgurjs.config.ts +0 -17
  77. package/examples/react-remote/src/Button.tsx +0 -15
  78. package/examples/react-remote/src/main.tsx +0 -6
  79. package/examples/react-remote/src/pages/Detail.tsx +0 -10
  80. package/examples/react-remote/src/pages/Home.tsx +0 -14
  81. package/examples/react-remote/src/utils.ts +0 -3
  82. package/examples/remote-a/fulgurjs.config.ts +0 -24
  83. package/examples/remote-a/src/views/Detail.vue +0 -3
  84. package/examples/remote-a/src/views/Home.vue +0 -3
@@ -8,7 +8,7 @@ import {
8
8
  import "./chunk-TSXZ7TWA.js";
9
9
 
10
10
  // src/vue-adapter.ts
11
- import { defineAsyncComponent, defineComponent, h } from "vue";
11
+ import { defineAsyncComponent, defineComponent, h, ref } from "vue";
12
12
  var ERROR_STYLE = {
13
13
  padding: "16px",
14
14
  border: "1px solid #fde2e2",
@@ -18,28 +18,111 @@ var ERROR_STYLE = {
18
18
  fontSize: "13px",
19
19
  lineHeight: "1.6"
20
20
  };
21
+ var BUTTON_STYLE = {
22
+ padding: "4px 14px",
23
+ marginRight: "8px",
24
+ border: "1px solid #c45656",
25
+ borderRadius: "4px",
26
+ background: "#c45656",
27
+ color: "#fff",
28
+ fontSize: "13px",
29
+ cursor: "pointer"
30
+ };
31
+ var SECONDARY_BUTTON_STYLE = {
32
+ padding: "4px 14px",
33
+ border: "1px solid #c45656",
34
+ borderRadius: "4px",
35
+ background: "#fff",
36
+ color: "#c45656",
37
+ fontSize: "13px",
38
+ cursor: "pointer"
39
+ };
21
40
  var RemoteErrorPlaceholder = defineComponent({
22
41
  name: "FulgurjsRemoteError",
23
- props: { error: { type: Object, default: void 0 } },
42
+ props: {
43
+ error: { type: Object, default: void 0 },
44
+ retry: { type: Function, required: false, default: void 0 },
45
+ retrying: { type: Boolean, default: false }
46
+ },
24
47
  setup(props) {
48
+ const reloadPage = () => {
49
+ window.location.reload();
50
+ };
25
51
  return () => {
26
52
  const err = props.error;
27
53
  const code = err?.code ?? "UNKNOWN";
28
54
  const message = err?.message ?? String(props.error ?? "unknown error");
29
- return h("div", { style: ERROR_STYLE }, [
55
+ const buttons = [
56
+ props.retry ? h("button", {
57
+ key: "retry",
58
+ style: BUTTON_STYLE,
59
+ type: "button",
60
+ "data-fulgurjs-retry": "",
61
+ disabled: props.retrying,
62
+ onClick: () => props.retry?.()
63
+ }, props.retrying ? "\u52A0\u8F7D\u4E2D\u2026" : "\u91CD\u8BD5\u52A0\u8F7D") : null,
64
+ h("button", {
65
+ key: "reload",
66
+ style: SECONDARY_BUTTON_STYLE,
67
+ type: "button",
68
+ "data-fulgurjs-reload": "",
69
+ disabled: props.retrying,
70
+ onClick: reloadPage
71
+ }, "\u5237\u65B0\u9875\u9762\u91CD\u8BD5")
72
+ ].filter(Boolean);
73
+ return h("div", { style: ERROR_STYLE, "data-fulgurjs-error": code }, [
30
74
  h("p", { style: "margin:0 0 4px;font-weight:600" }, `\u8FDC\u7A0B\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`),
31
75
  h("p", { style: "margin:0 0 8px;word-break:break-all" }, message),
32
- h("p", { style: "margin:0" }, "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002")
76
+ h("p", { style: "margin:0 0 8px" }, "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002"),
77
+ h("p", { style: "margin:0 0 10px" }, "\u300C\u91CD\u8BD5\u52A0\u8F7D\u300D\u5728\u5F53\u524D\u9875\u9762\u91CD\u5EFA\u52A0\u8F7D\uFF1B\u82E5\u6D4F\u89C8\u5668\u5DF2\u7F13\u5B58\u5931\u8D25\u7684\u6A21\u5757\uFF08\u5982\u8FDC\u7A0B\u9759\u6001\u5B50\u4F9D\u8D56\u66FE\u52A0\u8F7D\u5931\u8D25\uFF09\uFF0C\u8BF7\u7528\u300C\u5237\u65B0\u9875\u9762\u91CD\u8BD5\u300D\u2014\u2014\u5B83\u4F1A\u6574\u9875\u5237\u65B0\uFF08\u4FDD\u7559\u5F53\u524D\u5730\u5740\uFF09\uFF0C\u672A\u4FDD\u5B58\u7684\u9875\u9762\u72B6\u6001\u4F1A\u4E22\u5931\u3002"),
78
+ h("div", { style: "margin:0" }, buttons)
33
79
  ]);
34
80
  };
35
81
  }
36
82
  });
83
+ function createRecoverableErrorPlaceholder(loader, spec, userErrorComponent) {
84
+ if (userErrorComponent) return userErrorComponent;
85
+ return defineComponent({
86
+ name: "FulgurjsRemoteErrorWithRetry",
87
+ props: { error: { type: Object, default: void 0 } },
88
+ setup(props, { attrs }) {
89
+ const recovered = ref(null);
90
+ const retrying = ref(false);
91
+ const retryError = ref(void 0);
92
+ const retry = async () => {
93
+ if (retrying.value) return;
94
+ retrying.value = true;
95
+ retryError.value = void 0;
96
+ try {
97
+ const mod = await loader();
98
+ const inner = mod?.default ?? mod;
99
+ if (!inner) throw noRenderableExportError(spec, inner);
100
+ recovered.value = inner;
101
+ } catch (e) {
102
+ retryError.value = e;
103
+ } finally {
104
+ retrying.value = false;
105
+ }
106
+ };
107
+ return () => {
108
+ const inner = recovered.value;
109
+ if (inner) return h(inner, attrs);
110
+ return h(RemoteErrorPlaceholder, {
111
+ error: retryError.value ?? props.error,
112
+ retry,
113
+ retrying: retrying.value
114
+ });
115
+ };
116
+ }
117
+ });
118
+ }
37
119
  function createRemoteComponent(loadRemote) {
38
120
  return function remoteComponent(spec, opts = {}) {
121
+ const loader = () => loadRemote(spec, { retries: opts.retries }).then((m) => m.default ?? m);
39
122
  return defineAsyncComponent({
40
- loader: () => loadRemote(spec, { retries: opts.retries }).then((m) => m.default ?? m),
123
+ loader,
41
124
  loadingComponent: opts.loadingComponent,
42
- errorComponent: opts.errorComponent ?? RemoteErrorPlaceholder,
125
+ errorComponent: createRecoverableErrorPlaceholder(loader, spec, opts.errorComponent),
43
126
  delay: opts.delay,
44
127
  timeout: opts.timeout
45
128
  });
@@ -59,18 +142,19 @@ function createHostPages(options, load) {
59
142
  let comp = compCache.get(spec);
60
143
  if (!comp) {
61
144
  const name = cleanCompName(spec);
145
+ const pageLoader = async () => {
146
+ await beforeLoad?.();
147
+ const mod = await load(spec);
148
+ const inner = mod?.default ?? mod;
149
+ if (!inner) {
150
+ throw noRenderableExportError(spec, inner);
151
+ }
152
+ return inner;
153
+ };
62
154
  const asyncComp = defineAsyncComponent({
63
- loader: async () => {
64
- await beforeLoad?.();
65
- const mod = await load(spec);
66
- const inner = mod?.default ?? mod;
67
- if (!inner) {
68
- throw noRenderableExportError(spec, inner);
69
- }
70
- return inner;
71
- },
155
+ loader: pageLoader,
72
156
  loadingComponent,
73
- errorComponent: errorComponent ?? RemoteErrorPlaceholder,
157
+ errorComponent: createRecoverableErrorPlaceholder(pageLoader, spec, errorComponent),
74
158
  delay
75
159
  });
76
160
  asyncComp.name = name;
@@ -0,0 +1,47 @@
1
+ # fulgurjs-federation Examples
2
+
3
+ Pick your framework, copy the host and remote directories, run `npm install`, and you get a full module federation demo. All four projects are standalone: no dependency on this repo's source, the monorepo workspace, or any parent `node_modules` — the plugin dependency is an exact published version from the npm registry.
4
+
5
+ ## Vue (host 5214 / remote 5213)
6
+
7
+ Full walkthrough: **[vue/README.md](./vue/README.md)** (Chinese).
8
+
9
+ ```bash
10
+ cd examples/vue/remote && npm install && npm run dev # terminal 1: vue-remote, http://localhost:5213
11
+ cd examples/vue/host && npm install && npm run dev # terminal 2: vue-host, http://localhost:5214
12
+ ```
13
+
14
+ ## React (host 5204 / remote 5203)
15
+
16
+ Full walkthrough: **[react/README.md](./react/README.md)** (Chinese).
17
+
18
+ ```bash
19
+ cd examples/react/remote && npm install && npm run dev # terminal 1: react-remote, http://localhost:5203
20
+ cd examples/react/host && npm install && npm run dev # terminal 2: react-host, http://localhost:5204
21
+ ```
22
+
23
+ ## What each pair demonstrates
24
+
25
+ - A clickable remote counter component (local state lives inside the remote component; operated on the host page)
26
+ - Cross-application plain TS module calls (the host renders real return values)
27
+ - A remote home page plus a parameterized detail page (deep links survive refresh)
28
+ - Host navigation with route-level lazy loading (the home page never bulk-prefetches unvisited remote pages)
29
+ - The plugin's built-in loading/error placeholders and recovery actions (**Retry load** recovers in-page; **Refresh page to retry** recovers via a user-initiated full reload)
30
+ - Production build with a minimal Nginx deployment (including SPA fallback)
31
+
32
+ ## Layout
33
+
34
+ ```text
35
+ examples/
36
+ ├── README.md / README.en.md # this entry (CN/EN)
37
+ ├── vue/
38
+ │ ├── README.md # guide for the Vue pair
39
+ │ ├── host/ # Vue host (vue-host, 5214)
40
+ │ └── remote/ # Vue remote (vue-remote, 5213)
41
+ └── react/
42
+ ├── README.md # guide for the React pair
43
+ ├── host/ # React host (react-host, 5204)
44
+ └── remote/ # React remote (react-remote, 5203)
45
+ ```
46
+
47
+ > `examples/` is for users to copy and run. The repository-root `fixtures/` are internal regression harnesses (source-linked to the plugin workspace) — do not treat them as usage templates.
@@ -1,27 +1,49 @@
1
- # examples — 可复制的单项目配置样例
1
+ # fulgurjs-federation 示例总入口
2
2
 
3
- 每个目录是一个**独立项目**的 `fulgurjs.config.ts`(4.2.0 单项目契约:默认导出直接可传给 `federation()`):
3
+ 选择你的框架,复制对应宿主与远程两个目录,`npm install` 后即可看到完整的模块联邦演示。四个工程互相独立:不依赖本仓库源码、monorepo workspace 或父目录 node_modules,插件依赖为 npm registry 精确正式包。
4
4
 
5
- - [`remote-a/fulgurjs.config.ts`](./remote-a/fulgurjs.config.ts) — 远程应用:exposes / setup / shared
6
- - [`host/fulgurjs.config.ts`](./host/fulgurjs.config.ts) — 宿主应用:remotes 消费地址 + 可选 `hostPages` 页面核对数据(仅供 CLI)
5
+ ## Vue(宿主 5214 / 远程 5213)
7
6
 
8
- React 完整可运行工程(5.1.0 起,npm registry 安装即跑,区别于上面两个「配置样例」目录):
7
+ 完整操作步骤见 **[vue/README.md](./vue/README.md)**。
9
8
 
10
- - [`react-remote/`](./react-remote) — React 远程(5203):Button/utils/pages expose + shared react/react-dom singleton
11
- - [`react-host/`](./react-host) — React 宿主(5204):`@fulgurjs/federation/react` 导入 + 页面表 + React Router;运行说明见其 README
9
+ ```bash
10
+ cd examples/vue/remote && npm install && npm run dev # 终端 1:vue-remote,http://localhost:5213
11
+ cd examples/vue/host && npm install && npm run dev # 终端 2:vue-host,http://localhost:5214
12
+ ```
13
+
14
+ ## React(宿主 5204 / 远程 5203)
12
15
 
13
- 复制方式:把对应文件整份复制到**你自己的项目根目录**(两个项目不共享配置文件、不共享父目录配置),再在各自 `vite.config.ts` 的 plugins 里加一次 `federation(fulgurjsConfig)`:
16
+ 完整操作步骤见 **[react/README.md](./react/README.md)**。
14
17
 
15
- ```ts
16
- import federation from '@fulgurjs/federation'
17
- import fulgurjsConfig from './fulgurjs.config'
18
+ ```bash
19
+ cd examples/react/remote && npm install && npm run dev # 终端 1:react-remote,http://localhost:5203
20
+ cd examples/react/host && npm install && npm run dev # 终端 2:react-host,http://localhost:5204
21
+ ```
18
22
 
19
- export default defineConfig({
20
- plugins: [
21
- // ...原有插件,
22
- federation(fulgurjsConfig),
23
- ],
24
- })
23
+ ## 每对示例演示什么
24
+
25
+ - 远程可点击计数组件(本地状态在远程组件内,宿主页面直接操作)
26
+ - 普通 TS 模块跨应用调用(宿主展示真实返回值)
27
+ - 远程首页 + 带真实路由参数的详情页(深链刷新可恢复)
28
+ - 宿主导航、路由级懒加载(首页不批量预取未访问的远程页面)
29
+ - 插件默认加载/错误占位与恢复操作(**重试加载** 同页恢复;**刷新页面重试** 整页恢复)
30
+ - 生产构建 + 最小 Nginx 部署(含 SPA fallback)
31
+
32
+ ## 目录
33
+
34
+ ```text
35
+ examples/
36
+ ├── README.md / README.en.md # 本入口(中/英)
37
+ ├── vue/
38
+ │ ├── README.md # Vue 一对示例的组合说明
39
+ │ ├── host/ # Vue 宿主(vue-host,5214)
40
+ │ └── remote/ # Vue 远程(vue-remote,5213)
41
+ └── react/
42
+ ├── README.md # React 一对示例的组合说明
43
+ ├── host/ # React 宿主(react-host,5204)
44
+ └── remote/ # React 远程(react-remote,5203)
25
45
  ```
26
46
 
27
- 验证:在复制后的项目根目录运行 `npx fulgurjs explain`(纯本地、无网络),应输出本应用的形态摘要与 `federation(fulgurjsConfig)` 接入块。
47
+ > `examples/` 面向使用者拷贝运行;仓库根的 `fixtures/` 是插件内部回归夹具(源码 link 到插件工作区),请勿当作使用模板。
48
+
49
+ > 提示:复制后可在项目根目录运行 `npx fulgurjs explain`(纯本地、无网络)核对联邦配置摘要。
@@ -0,0 +1,83 @@
1
+ # React 联邦示例:宿主 + 远程(可直接复制运行)
2
+
3
+ 本目录是一对完整、可独立安装运行的 React 19 模块联邦演示:
4
+
5
+ | 目录 | 角色 | 端口 | 说明 |
6
+ | --- | --- | --- | --- |
7
+ | [`remote/`](./remote/) | **React 远程**(被消费方) | 5203 | 暴露可点击按钮组件、普通 TS 工具模块、联邦首页与参数详情页 |
8
+ | [`host/`](./host/) | **React 宿主**(消费方) | 5204 | 消费 react-remote 的组件/模块/页面,提供导航与懒加载 |
9
+
10
+ - 包管理器统一为 **npm**;插件依赖为 **registry 精确正式包**(`@fulgurjs/federation`,见各 `package.json`),不依赖本仓库源码、monorepo workspace 或父目录 node_modules。
11
+ - 把 `host/` 与 `remote/` 复制到**两个互不共享父目录的目录**后,各自 `npm install` 仍可运行。
12
+
13
+ ## 最快开始(从干净拷贝)
14
+
15
+ ```bash
16
+ # 终端 1 —— 先启动远程
17
+ cd react/remote
18
+ npm install
19
+ npm run dev # http://localhost:5203
20
+
21
+ # 终端 2 —— 再启动宿主
22
+ cd react/host
23
+ npm install
24
+ npm run dev # http://localhost:5204
25
+ ```
26
+
27
+ 打开 <http://localhost:5204/>:
28
+
29
+ - 「首页」里有来自 react-remote 的**可点击计数按钮**(进入页面才按需加载)。
30
+ - 「远程首页 / 远程详情」是 react-remote 暴露的**联邦页面**;详情页演示真实路由参数(`/remote/detail/7?tab=basic`)。
31
+ - 「TS 模块调用」演示 `useLoadRemote` 调用 react-remote 暴露的普通 TS 模块。
32
+ - 停掉远程 dev server 再刷新宿主页面 → 插件默认错误占位出现,提供**「重试加载」**(同页恢复)与**「刷新页面重试」**(整页恢复)两个操作;重启远程后点击即可恢复业务。
33
+
34
+ ## 演示的功能点
35
+
36
+ | 功能 | 位置 |
37
+ | --- | --- |
38
+ | 远程可点击组件(Hooks 计数) | `remote/src/exposes/ClickButton.tsx`,宿主首页消费 |
39
+ | 普通 TS 模块调用(useLoadRemote) | `remote/src/exposes/utils.ts`,`host/src/pages/UtilsDemo.tsx` |
40
+ | 联邦页面(远程首页 / 带参数详情页) | `remote/src/exposes/pages/`,页面表见 `host/fulgurjs.config.ts` |
41
+ | 宿主导航 + 懒加载(不批量预取) | `host/src/main.tsx`(路由级按需加载) |
42
+ | 默认加载/错误/恢复占位 | 插件内置(`remoteComponent` / `createReactHostPages`),示例不自建错误组件 |
43
+
44
+ ## 生产构建与最小 Nginx 部署
45
+
46
+ ```bash
47
+ cd react/remote && npm run build -- --base=/react-remote/ # 产物 remote/dist/(子路径 base 与部署位置对应)
48
+ cd react/host && npm run build # 产物 host/dist/
49
+ ```
50
+
51
+ 部署形态:宿主部署在站点根 `/`,远程部署在子路径 `/react-remote/`(与 `host/fulgurjs.config.ts` 的 `prod: '/react-remote'` 对应)。最小 Nginx 配置:
52
+
53
+ ```nginx
54
+ server {
55
+ listen 8081;
56
+
57
+ root /srv/react-demo/host;
58
+ index index.html;
59
+
60
+ location /react-remote/ {
61
+ alias /srv/react-demo/remote/;
62
+ try_files $uri $uri/ =404;
63
+ }
64
+
65
+ # 宿主 SPA fallback:深链刷新回退到 index.html
66
+ location / {
67
+ try_files $uri /index.html;
68
+ }
69
+ }
70
+ ```
71
+
72
+ 部署后访问 `http://<host>:8081/`,深链 `http://<host>:8081/remote/detail/7?tab=basic` 直接刷新也能到达同一页面。
73
+
74
+ ## HMR 边界(dev)
75
+
76
+ - 远程组件修改(文本/样式/Hooks 结构不变的改动)自动热更新到**正在显示的宿主页面**,组件本地状态保留,无整页刷新(5.2.0 起跨源 Fast Refresh 由插件保证单一 react-refresh 实例)。
77
+ - 远程普通 TS 模块修改同样自动传播到引用它的组件边界。
78
+ - React Refresh 不兼容的导出/Hooks 结构变化、Vite 要求 full-reload 的改动会按框架标准重新挂载或整页刷新——不承诺任意改动保活。
79
+
80
+ ## examples 与 fixtures 的区别
81
+
82
+ `examples/` 面向**使用者**:拷贝、安装 registry 正式包、独立运行。
83
+ 仓库根的 `fixtures/` 是**插件内部回归夹具**(源码 `link:` 到插件工作区),不要当作使用模板。
@@ -0,0 +1,49 @@
1
+ # react-host —— React 宿主(fulgurjs 联邦演示)
2
+
3
+ > **角色:React 宿主(消费方)。** 消费 [`../remote/`](../remote/)(react-remote,端口 5203)暴露的组件、TS 模块与联邦页面;本应用自身不暴露任何模块。
4
+
5
+ ## 从干净拷贝运行
6
+
7
+ 前置:Node ≥ 20.19 与 npm。
8
+
9
+ ```bash
10
+ # 先启动远程(另一终端):见 ../remote/README.md
11
+ cd react-host # 把本目录复制到任意位置后进入
12
+ npm install
13
+ npm run dev # http://localhost:5204
14
+ ```
15
+
16
+ 成功画面:导航条含「首页 / 远程首页 / 远程详情 / TS 模块调用」;「远程首页」出现 react-remote 的标题与可点击计数按钮。
17
+
18
+ ## 目录与关键配置
19
+
20
+ | 文件 | 职责 |
21
+ | --- | --- |
22
+ | `fulgurjs.config.ts` | **联邦唯一声明**:remotes 地址(dev/prod)、页面表 `pages`、前缀归属 `remotePrefixes` |
23
+ | `vite.config.ts` | 标准 Vite 配置,`federation(fulgurjsConfig)` 挂载插件 |
24
+ | `src/remotePages.tsx` | `createReactHostPages({ pages, remotePrefixes })`(模块顶层一次);`component(spec)` 取远程页面组件 |
25
+ | `src/App.tsx` | `remoteComponent('react-remote/ClickButton')` 消费单个远程组件 |
26
+ | `src/pages/UtilsDemo.tsx` | `useLoadRemote('react-remote/utils')` 调用普通 TS 模块 |
27
+ | `src/main.tsx` | react-router 路由;参数页把 `params`/`query` 透传给远程组件 |
28
+
29
+ ## API 职责速查
30
+
31
+ - `remoteComponent(spec, opts?)`:单个远程组件;失败时插件内置中文错误占位(错误码 + 修法 + **重试加载** + **刷新页面重试**)。
32
+ - `useLoadRemote(spec)`:hook 形态的模块加载(`data/error/loading/reload`),适合普通 TS 模块。
33
+ - `createReactHostPages(...)` + `component(spec)`:页面表驱动的联邦页面。
34
+
35
+ ## 生产构建与部署
36
+
37
+ ```bash
38
+ npm run build # dist/
39
+ ```
40
+
41
+ 部署在站点根 `/`(Nginx SPA fallback 见[组合说明](../README.md#生产构建与最小-nginx-部署));深链刷新由 fallback + 插件路由解析恢复。
42
+
43
+ ## 恢复操作(故障演练)
44
+
45
+ 停掉 react-remote → 刷新宿主进入远程页面 → 默认错误占位出现 → 重启 react-remote → 点「重试加载」同页恢复;静态子依赖曾失败等场景点「刷新页面重试」整页恢复。
46
+
47
+ ## HMR(5.2.0 起)
48
+
49
+ react-remote 的组件修改(文本/样式/Hooks 结构不变)自动热更新到本宿主正在显示的页面并**保留组件本地状态**(插件保证全页单一 react-refresh 实例);普通 TS 模块修改自动传播到引用它的组件边界;不兼容改动按框架标准重新挂载/整页刷新。
@@ -0,0 +1,31 @@
1
+ import type { FederationOptions, PageRouteLike } from '@fulgurjs/federation'
2
+
3
+ /**
4
+ * 宿主端联邦配置(唯一需要维护的联邦声明文件)。
5
+ * - remotes:改远程地址只动这里;dev 与生产可以分别指定。
6
+ * - hostPages:页面表 + 路由前缀归属,浏览器侧 createReactHostPages 与 CLI check-pages 共用。
7
+ */
8
+ export default {
9
+ name: 'react-host',
10
+ remotes: {
11
+ 'react-remote': {
12
+ dev: 'http://localhost:5203',
13
+ prod: '/react-remote',
14
+ },
15
+ },
16
+ shared: {
17
+ react: { singleton: true },
18
+ 'react-dom': { singleton: true },
19
+ },
20
+ } satisfies FederationOptions
21
+
22
+ /** 页面表:URL 路由 → 远程 expose 的映射(spec = 远程名 + exposes 键) */
23
+ export const pages: PageRouteLike[] = [
24
+ { route: '/remote/home', spec: 'pages/HomePage', name: 'RemoteHome', title: '远程首页' },
25
+ { route: '/remote/detail/:id', spec: 'pages/DetailPage', name: 'RemoteDetail', title: '远程详情' },
26
+ ]
27
+
28
+ /** 路由前缀 → 远程归属(最长前缀匹配) */
29
+ export const remotePrefixes: Record<string, string> = {
30
+ '/remote': 'react-remote',
31
+ }
@@ -2,7 +2,8 @@
2
2
  <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <title>react-remote(fulgurjs example)</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>react-host(fulgurjs 演示宿主)</title>
6
7
  </head>
7
8
  <body>
8
9
  <div id="root"></div>