@ice/mf-runtime 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -1,15 +1,226 @@
1
- # @ali/ice-mf-runtime
1
+ # @ice/mf-runtime
2
2
 
3
- 组件功能描述
3
+ 基于 Module Federation 的运行时工具,用于在 ice.js 应用中加载和管理远程模块。支持跨版本 React 组件加载
4
4
 
5
- ## Install
5
+ ## 特性
6
+
7
+ - 基于 [Module Federation 2.0](https://module-federation.io/index.html)
8
+ - 支持跨版本 React 组件加载
9
+ - 内置冲突检测和降级渲染
10
+
11
+ ## 安装
6
12
 
7
13
  ```bash
8
- $ npm i @ali/ice-mf-runtime --save
14
+ $ npm i @ice/mf-runtime --save
15
+ ```
16
+
17
+ ## 使用
18
+
19
+ ### 1. 初始化配置
20
+
21
+ 在应用入口处初始化 Module Federation 配置:
22
+
23
+ ```tsx
24
+ import { init } from '@ice/mf-runtime';
25
+
26
+ init({
27
+ name: 'host_app', // 当前应用名称
28
+ remotes: [
29
+ {
30
+ name: 'remote_app', // 远程应用名称
31
+ entry: 'http://localhost:3000/remoteEntry.js', // 远程应用入口文件,也支持新的 mf-manifest.json 结构
32
+ }
33
+ ]
34
+ });
35
+ ```
36
+
37
+ ### 2. 加载远程模块
38
+
39
+ 使用 `RemoteModule` 组件加载远程模块:
40
+
41
+ ```tsx
42
+ import { RemoteModule } from '@ice/mf-runtime';
43
+
44
+ function App() {
45
+ return (
46
+ <div>
47
+ <h1>Host Application</h1>
48
+ <RemoteModule
49
+ module="Widget" // 远程模块名称
50
+ scope="remote_app" // 远程应用名称
51
+ LoadingComponent={<div>Loading...</div>} // 可选的加载状态组件
52
+ />
53
+ </div>
54
+ );
55
+ }
9
56
  ```
10
57
 
11
- ## Usage
58
+ ### 3. 高级配置
12
59
 
13
- ```jsx
14
- import IceMfRuntime from '@ali/ice-mf-runtime';
60
+ #### 自定义 Public Path
61
+
62
+ 如果远程模块的静态资源需要使用特定的 CDN 地址,可以通过 `publicPath` 属性指定:
63
+
64
+ ```tsx
65
+ <RemoteModule
66
+ module="Widget"
67
+ scope="remote_app"
68
+ publicPath="https://cdn.example.com/remote-app/"
69
+ />
70
+ ```
71
+
72
+ #### 远程模块高级配置
73
+
74
+ 通过 `init` 方法的 `extraInfo` 配置,可以实现更多高级特性:
75
+
76
+ ##### 类型定义
77
+
78
+ ```ts
79
+ interface ExtraInfo {
80
+ // 配置外部依赖
81
+ external?: {
82
+ react?: string; // 外部 React 全局变量名
83
+ 'react-dom'?: string; // 外部 ReactDOM 全局变量名
84
+ };
85
+ // 是否为旧版本模块(用于兼容性处理)
86
+ legacy?: boolean;
87
+ }
15
88
  ```
89
+
90
+ ##### 1. 使用外部依赖
91
+
92
+ 当远程模块需要使用 CDN 加载的 React 时,可以通过 `external` 配置指定:
93
+
94
+ ```tsx
95
+ init({
96
+ name: 'host_app',
97
+ remotes: [
98
+ {
99
+ name: 'remote_app',
100
+ entry: 'http://localhost:3000/remoteEntry.js',
101
+ extraInfo: {
102
+ external: {
103
+ react: 'React', // 使用全局的 React
104
+ 'react-dom': 'ReactDOM' // 使用全局的 ReactDOM
105
+ }
106
+ }
107
+ }
108
+ ]
109
+ });
110
+ ```
111
+
112
+ 当使用模块的 host 应用没有启用 MF 规范时,你可能会遇到 React 多实例引起的问题,对于这类问题,推荐使用外部依赖配置来保证 host 应用和 remote 应用使用同一个环境的 React 实例。
113
+
114
+ 此时需要保证环境中存在指定的全局 React。runtime 会自动处理全局多个 React 的兼容性,优先使用兼容的 React 版本渲染。
115
+
116
+ ##### 2. 兼容旧版本模块
117
+
118
+ 对于一些旧版本的远程模块,可能需要特殊的兼容性处理,此时可以启用 `legacy` 模式:
119
+
120
+ 目前 legacy 模式下,会做以下处理:
121
+ 1. 兼容 remote 模块 expose name 不以 `./` 开头的情况。
122
+
123
+ ```tsx
124
+ init({
125
+ name: 'host_app',
126
+ remotes: [
127
+ {
128
+ name: 'remote_app',
129
+ entry: 'http://localhost:3000/remoteEntry.js',
130
+ extraInfo: {
131
+ legacy: true // 启用兼容模式
132
+ }
133
+ }
134
+ ]
135
+ });
136
+ ```
137
+
138
+ ##### 3. 混合配置示例
139
+
140
+ 在实际项目中,可能需要同时配置多个远程模块,每个模块使用不同的配置:
141
+
142
+ ```tsx
143
+ init({
144
+ name: 'host_app',
145
+ remotes: [
146
+ {
147
+ name: 'remote_app',
148
+ entry: 'http://localhost:3000/remoteEntry.js',
149
+ extraInfo: {
150
+ external: {
151
+ react: 'React',
152
+ 'react-dom': 'ReactDOM'
153
+ }
154
+ }
155
+ },
156
+ {
157
+ name: 'remote_app_2',
158
+ entry: 'http://localhost:3002/remoteEntry.js',
159
+ extraInfo: {
160
+ legacy: true
161
+ }
162
+ }
163
+ ]
164
+ });
165
+ ```
166
+
167
+ #### 为某个模块指定 React 版本渲染
168
+
169
+ 当远程模块使用不同版本的 React 时,可以通过 `runtime` 属性指定运行时:
170
+
171
+ ```tsx
172
+ import { RemoteModule } from '@ice/mf-runtime';
173
+ import * as React from 'react';
174
+ import * as ReactDOM from 'react-dom';
175
+
176
+ function App() {
177
+ return (
178
+ <RemoteModule
179
+ module="Widget"
180
+ scope="remote_app"
181
+ runtime={{
182
+ react: React,
183
+ reactDOM: ReactDOM
184
+ }}
185
+ />
186
+ );
187
+ }
188
+ ```
189
+ ## API
190
+
191
+ ### init(options)
192
+
193
+ 初始化配置项:
194
+
195
+ | 参数 | 说明 | 类型 | 默认值 |
196
+ |------|------|------|--------|
197
+ | name | 当前应用名称 | `string` | - |
198
+ | remotes | 远程应用配置数组 | `Array<RemoteConfig>` | `[]` |
199
+
200
+ RemoteConfig 配置:
201
+
202
+ | 参数 | 说明 | 类型 | 默认值 |
203
+ |------|------|------|--------|
204
+ | name | 远程应用名称 | `string` | - |
205
+ | entry | 远程应用入口文件地址 | `string` | - |
206
+ | extraInfo | 额外配置信息,详见[远程模块高级配置](#远程模块高级配置) | `ExtraInfo` | - |
207
+
208
+ ### RemoteModule
209
+
210
+ 组件属性:
211
+
212
+ | 参数 | 说明 | 类型 | 默认值 |
213
+ |------|------|------|--------|
214
+ | module | 远程模块名称 | `string` | - |
215
+ | scope | 远程应用名称 | `string` | - |
216
+ | runtime | React 运行时配置 | `{ react: React, reactDOM: ReactDOM }` | - |
217
+ | publicPath | 远程模块静态资源地址 | `string` | - |
218
+ | LoadingComponent | 加载状态组件 | `ReactNode` | `<div>Loading...</div>` |
219
+ | ErrorComponent | 错误回退组件 | `ReactNode` | `<div>远程模块加载失败: {error.message}</div>` |
220
+ | onError | 错误处理回调函数 | `(error: Error, info: { componentStack: string }) => void` | - |
221
+
222
+
223
+ ## 注意事项
224
+
225
+ 1. host name 请保证全局唯一。
226
+ 2. remote name 的 package.json name 请保证全局唯一,否则会出现意料之外的缓存问题。
@@ -8,6 +8,10 @@ interface RemoteModuleOptions {
8
8
  };
9
9
  publicPath?: string;
10
10
  LoadingComponent?: React.ReactNode;
11
+ ErrorComponent?: React.ReactNode;
12
+ onError?: (error: Error, info: {
13
+ componentStack: string;
14
+ }) => void;
11
15
  }
12
- export declare const RemoteModule: ({ module, scope, runtime, publicPath, LoadingComponent, }: RemoteModuleOptions) => string | number | true | Iterable<React.ReactNode> | React.JSX.Element;
16
+ export declare const RemoteModule: ({ module, scope, runtime, publicPath, LoadingComponent, ErrorComponent, onError, }: RemoteModuleOptions) => string | number | true | Iterable<React.ReactNode> | React.JSX.Element;
13
17
  export {};
@@ -1,7 +1,8 @@
1
- import { jsx as _jsx } from "@ice/jsx-runtime/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@ice/jsx-runtime/jsx-runtime";
2
2
  import { loadRemote } from '@module-federation/runtime';
3
3
  import * as React from 'react';
4
4
  import { useEffect, useState } from 'react';
5
+ import { ErrorBoundary } from 'react-error-boundary';
5
6
  import { FallBack } from './FallBack';
6
7
  import { setFederatedModulePublicPath } from './set-public-path';
7
8
  function useDynamicImport({ module, scope }) {
@@ -23,7 +24,7 @@ function useDynamicImport({ module, scope }) {
23
24
  ]);
24
25
  return component;
25
26
  }
26
- export const RemoteModule = ({ module, scope, runtime, publicPath, LoadingComponent })=>{
27
+ export const RemoteModule = ({ module, scope, runtime, publicPath, LoadingComponent, ErrorComponent, onError })=>{
27
28
  var _remoteModule;
28
29
  if (publicPath) {
29
30
  setFederatedModulePublicPath(scope, publicPath);
@@ -53,10 +54,20 @@ export const RemoteModule = ({ module, scope, runtime, publicPath, LoadingCompon
53
54
  const Loading = LoadingComponent || /*#__PURE__*/ _jsx("div", {
54
55
  children: "Loading..."
55
56
  });
57
+ const ErrorFallback = ({ error })=>ErrorComponent || /*#__PURE__*/ _jsxs("div", {
58
+ children: [
59
+ "远程模块加载失败: ",
60
+ error.message
61
+ ]
62
+ });
56
63
  if (Component) {
57
- return /*#__PURE__*/ _jsx(React.Suspense, {
58
- fallback: Loading,
59
- children: /*#__PURE__*/ _jsx(Component, {})
64
+ return /*#__PURE__*/ _jsx(ErrorBoundary, {
65
+ FallbackComponent: ErrorFallback,
66
+ onError: onError,
67
+ children: /*#__PURE__*/ _jsx(React.Suspense, {
68
+ fallback: Loading,
69
+ children: /*#__PURE__*/ _jsx(Component, {})
70
+ })
60
71
  });
61
72
  }
62
73
  return Loading;
package/es2017/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { registerPlugins, init as mfInit } from '@module-federation/runtime';
2
2
  import { runtimePlugin } from './runtime-plugin';
3
+ import { initGlobalStore } from './mf-global-store';
3
4
  export { loadRemote, registerPlugins } from '@module-federation/runtime';
4
5
  export * from './FallBack';
5
6
  export * from './RemoteModule';
6
7
  export function init(options) {
8
+ initGlobalStore(options);
7
9
  mfInit(options);
8
10
  registerPlugins([
9
11
  runtimePlugin()
@@ -8,6 +8,10 @@ interface RemoteModuleOptions {
8
8
  };
9
9
  publicPath?: string;
10
10
  LoadingComponent?: React.ReactNode;
11
+ ErrorComponent?: React.ReactNode;
12
+ onError?: (error: Error, info: {
13
+ componentStack: string;
14
+ }) => void;
11
15
  }
12
- export declare const RemoteModule: ({ module, scope, runtime, publicPath, LoadingComponent, }: RemoteModuleOptions) => string | number | true | Iterable<React.ReactNode> | React.JSX.Element;
16
+ export declare const RemoteModule: ({ module, scope, runtime, publicPath, LoadingComponent, ErrorComponent, onError, }: RemoteModuleOptions) => string | number | true | Iterable<React.ReactNode> | React.JSX.Element;
13
17
  export {};
@@ -1,10 +1,11 @@
1
1
  import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator";
2
2
  import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array";
3
3
  import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
4
- import { jsx as _jsx } from "@ice/jsx-runtime/jsx-runtime";
4
+ import { jsx as _jsx, jsxs as _jsxs } from "@ice/jsx-runtime/jsx-runtime";
5
5
  import { loadRemote } from "@module-federation/runtime";
6
6
  import * as React from "react";
7
7
  import { useEffect, useState } from "react";
8
+ import { ErrorBoundary } from "react-error-boundary";
8
9
  import { FallBack } from "./FallBack";
9
10
  import { setFederatedModulePublicPath } from "./set-public-path";
10
11
  function useDynamicImport(param) {
@@ -61,7 +62,7 @@ function useDynamicImport(param) {
61
62
  return component;
62
63
  }
63
64
  export var RemoteModule = function(param) {
64
- var module = param.module, scope = param.scope, runtime = param.runtime, publicPath = param.publicPath, LoadingComponent = param.LoadingComponent;
65
+ var module = param.module, scope = param.scope, runtime = param.runtime, publicPath = param.publicPath, LoadingComponent = param.LoadingComponent, ErrorComponent = param.ErrorComponent, onError = param.onError;
65
66
  var _remoteModule;
66
67
  if (publicPath) {
67
68
  setFederatedModulePublicPath(scope, publicPath);
@@ -97,10 +98,23 @@ export var RemoteModule = function(param) {
97
98
  var Loading = LoadingComponent || /*#__PURE__*/ _jsx("div", {
98
99
  children: "Loading..."
99
100
  });
101
+ var ErrorFallback = function(param) {
102
+ var error = param.error;
103
+ return ErrorComponent || /*#__PURE__*/ _jsxs("div", {
104
+ children: [
105
+ "远程模块加载失败: ",
106
+ error.message
107
+ ]
108
+ });
109
+ };
100
110
  if (Component) {
101
- return /*#__PURE__*/ _jsx(React.Suspense, {
102
- fallback: Loading,
103
- children: /*#__PURE__*/ _jsx(Component, {})
111
+ return /*#__PURE__*/ _jsx(ErrorBoundary, {
112
+ FallbackComponent: ErrorFallback,
113
+ onError: onError,
114
+ children: /*#__PURE__*/ _jsx(React.Suspense, {
115
+ fallback: Loading,
116
+ children: /*#__PURE__*/ _jsx(Component, {})
117
+ })
104
118
  });
105
119
  }
106
120
  return Loading;
package/esm/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { registerPlugins, init as mfInit } from "@module-federation/runtime";
2
2
  import { runtimePlugin } from "./runtime-plugin";
3
+ import { initGlobalStore } from "./mf-global-store";
3
4
  export { loadRemote, registerPlugins } from "@module-federation/runtime";
4
5
  export * from "./FallBack";
5
6
  export * from "./RemoteModule";
6
7
  export function init(options) {
8
+ initGlobalStore(options);
7
9
  mfInit(options);
8
10
  registerPlugins([
9
11
  runtimePlugin()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ice/mf-runtime",
3
- "version": "0.0.1",
4
- "description": "组件功能描述",
3
+ "version": "0.0.3",
4
+ "description": "ice mf runtime",
5
5
  "files": [
6
6
  "esm",
7
7
  "es2017",
@@ -48,7 +48,8 @@
48
48
  "@ice/jsx-runtime": "^0.2.0",
49
49
  "@module-federation/runtime": "^0.11.2",
50
50
  "@module-federation/runtime-core": "^0.11.2",
51
- "@swc/helpers": "^0.5.1"
51
+ "@swc/helpers": "^0.5.1",
52
+ "react-error-boundary": "^5.0.0"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@ali/pkg-plugin-dev": "^1.0.0",
@@ -64,7 +65,8 @@
64
65
  "stylelint": "^15.0.0"
65
66
  },
66
67
  "publishConfig": {
67
- "access": "public"
68
+ "access": "public",
69
+ "registry": "https://registry.npmjs.org"
68
70
  },
69
71
  "peerDependencies": {
70
72
  "react": "^16 || ^17 || ^18"