Skip to content

Commit 20c51c3

Browse files
authored
fix(bridge-react): remove the unnecessary bundle for react-router-dom when user not using react-router in bridge-react (#4227)
1 parent f53f239 commit 20c51c3

File tree

21 files changed

+671
-232
lines changed

21 files changed

+671
-232
lines changed

apps/website-new/docs/en/guide/framework/modernjs.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ The Modern.js plugin re-exports `@module-federation/bridge-react` from `@module-
9191
### createRemoteComponent <Badge type="danger">Deprecated</Badge>
9292

9393
::: danger
94-
This API has been deprecated. Please use [createLazyComponent](/practice/bridge/react-bridge/load-component.html#createlazycomponent-vs-createremoteappcomponent) instead.
94+
This API has been deprecated. Please use [createLazyComponent](/practice/bridge/react-bridge/load-component.html#what-is-createlazycomponent) instead.
9595
:::
9696

9797
#### Migration Guide

apps/website-new/docs/en/practice/bridge/react-bridge/load-app.mdx

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,112 @@ interface RemoteComponentProps<T = Record<string, unknown>> {
310310
loader: () => loadRemote('remote1/export-app'),
311311
export: 'dashboard'
312312
})
313-
```
313+
```
314+
315+
## Bundle Size Optimization
316+
317+
### React Router Dependency Explanation
318+
319+
By default, `@module-federation/bridge-react` includes `react-router-dom` in your bundle to provide the following out-of-the-box capabilities:
320+
321+
-Automatic basename injection - No manual route base path configuration needed
322+
-Router context passing - Automatic React Router context handling
323+
-Nested routing support - Complete router integration capabilities
324+
325+
**However**, if your project meets any of these conditions:
326+
- Doesn't need routing functionality (pure component loading)
327+
- Uses a non-react-router routing framework (e.g., TanStack Router)
328+
- Wants to minimize bundle size
329+
330+
**We recommend** disabling the `enableBridgeRouter` configuration to turn off this capability, which will:
331+
-Reduce bundle size by ~3KB (gzipped)
332+
-Avoid unnecessary dependency injection
333+
-Eliminate potential version conflict risks
334+
335+
### How to Disable Router Dependency
336+
337+
You can control whether to include router support through the `bridge.enableBridgeRouter` configuration:
338+
339+
```ts title="rsbuild.config.ts"
340+
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
341+
342+
export default {
343+
plugins: [
344+
pluginModuleFederation({
345+
name: 'host-app',
346+
remotes: {
347+
remote1: 'remote1@http://localhost:3001/mf-manifest.json',
348+
},
349+
bridge: {
350+
// Disable router support to reduce bundle size
351+
enableBridgeRouter: false,
352+
},
353+
}),
354+
],
355+
};
356+
```
357+
358+
:::tip Configuration Behavior
359+
- **`enableBridgeRouter: false`**: Automatically aliases to `/base` entry point (no react-router-dom code)
360+
- **`enableBridgeRouter: true`** or **`undefined`**: Includes router support (default behavior)
361+
:::
362+
363+
### When to Disable Router?
364+
365+
**Disable router** (`enableBridgeRouter: false`) when:
366+
-Your application doesn't use react-router
367+
-You want to minimize bundle size
368+
-You can manually manage basename if needed
369+
370+
**Keep router enabled** (default) when:
371+
-Your application uses react-router
372+
-You need automatic basename injection
373+
-You need routing context integration
374+
375+
### Migration Example
376+
377+
#### Before: With Router (Default)
378+
```tsx
379+
import { createRemoteAppComponent } from '@module-federation/bridge-react';
380+
381+
const RemoteApp = createRemoteAppComponent({
382+
loader: () => loadRemote('remote1/app'),
383+
loading: <div>Loading...</div>,
384+
fallback: ErrorBoundary,
385+
});
386+
387+
// basename automatically retrieved from router context
388+
<RemoteApp />
389+
```
390+
391+
#### After: Without Router (Optimized)
392+
```ts title="rsbuild.config.ts"
393+
// Configuration
394+
pluginModuleFederation({
395+
bridge: {
396+
enableBridgeRouter: false, // Disable router
397+
},
398+
})
399+
```
400+
401+
```tsx
402+
import { createRemoteAppComponent } from '@module-federation/bridge-react';
403+
404+
const RemoteApp = createRemoteAppComponent({
405+
loader: () => loadRemote('remote1/app'),
406+
loading: <div>Loading...</div>,
407+
fallback: ErrorBoundary,
408+
});
409+
410+
// No changes needed! The plugin automatically aliases to /base entry
411+
<RemoteApp basename="/" /> // Manually pass basename if needed
412+
```
413+
414+
:::info How It Works
415+
When `enableBridgeRouter: false`, the Module Federation plugin automatically sets up a webpack alias:
416+
```
417+
'@module-federation/bridge-react' → '@module-federation/bridge-react/base'
418+
```
419+
420+
This means your imports automatically resolve to the router-free version without changing any code!
421+
:::

apps/website-new/docs/zh/guide/framework/modernjs.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export default App;
127127
### createRemoteSSRComponent <Badge type="danger">废弃</Badge>
128128

129129
::: danger
130-
此 API 已被废弃,请使用[createLazyComponent](../../practice/bridge/react-bridge#createlazycomponent)
130+
此 API 已被废弃,请使用[createLazyComponent](/practice/bridge/react-bridge/load-component.html#什么是-createlazycomponent)
131131
:::
132132

133133
#### 如何迁移

apps/website-new/docs/zh/practice/bridge/react-bridge.mdx

Whitespace-only changes.

apps/website-new/docs/zh/practice/bridge/react-bridge/load-app.mdx

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ function createRemoteAppComponent<T = Record<string, unknown>, E extends keyof T
183183
>
184184
```
185185

186-
### RemoteComponentParams\<T, E\>
186+
#### RemoteComponentParams\<T, E\>
187187

188188
配置参数接口:
189189

@@ -206,7 +206,7 @@ interface RemoteComponentParams<T = Record<string, unknown>, E extends keyof T =
206206
}
207207
```
208208

209-
### RemoteComponentProps\<T\>
209+
#### RemoteComponentProps\<T\>
210210

211211
返回组件的属性接口:
212212

@@ -241,7 +241,7 @@ interface RemoteComponentProps<T = Record<string, unknown>> {
241241

242242
### 参数详解
243243

244-
### loader
244+
#### loader
245245

246246
- **类型**: `() => Promise<T>`
247247
- **必需**: 是
@@ -252,7 +252,7 @@ interface RemoteComponentProps<T = Record<string, unknown>> {
252252
loader: () => import('remote1/export-app')
253253
```
254254

255-
### loading
255+
#### loading
256256

257257
- **类型**: `React.ReactNode`
258258
- **必需**: 是
@@ -264,7 +264,7 @@ interface RemoteComponentProps<T = Record<string, unknown>> {
264264
loading: <Spinner />
265265
```
266266

267-
### fallback
267+
#### fallback
268268

269269
- **类型**: `React.ComponentType<{ error: Error }>`
270270
- **必需**: 是
@@ -275,7 +275,7 @@ interface RemoteComponentProps<T = Record<string, unknown>> {
275275
fallback: ErrorBoundaryComponent
276276
```
277277

278-
### export
278+
#### export
279279

280280
- **类型**: `E extends keyof T` (泛型约束,通常是 `string`)
281281
- **必需**: 否
@@ -310,4 +310,114 @@ interface RemoteComponentProps<T = Record<string, unknown>> {
310310
loader: () => loadRemote('remote1/export-app'),
311311
export: 'dashboard'
312312
})
313-
```
313+
```
314+
315+
316+
## Bundle 体积优化
317+
318+
### React Router 依赖说明
319+
320+
默认情况下,`@module-federation/bridge-react` 会将 `react-router-dom` 打包到你的 bundle 中,这是为了提供以下开箱即用的能力:
321+
322+
- ✅ 自动 basename 注入 - 无需手动配置路由基础路径
323+
- ✅ 路由上下文传递 - 自动处理 React Router 上下文
324+
- ✅ 嵌套路由支持 - 完整的路由集成能力
325+
326+
**但是**,如果你的项目满足以下任一条件:
327+
- 不需要路由功能(纯组件加载)
328+
- 使用非 react-router 的路由框架(如 TanStack Router
329+
- 希望最小化 bundle 体积
330+
331+
**建议关闭** `enableBridgeRouter` 配置来禁用此能力,这将:
332+
- ✅ 减小 bundle 体积约 3KB (gzipped)
333+
- ✅ 避免不必要的依赖注入
334+
- ✅ 消除潜在的版本冲突风险
335+
336+
### 如何禁用 Router 依赖
337+
338+
你可以通过 `bridge.enableBridgeRouter` 配置来控制是否包含路由支持:
339+
340+
```ts title="rsbuild.config.ts"
341+
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
342+
343+
export default {
344+
plugins: [
345+
pluginModuleFederation({
346+
name: 'host-app',
347+
remotes: {
348+
remote1: 'remote1@http://localhost:3001/mf-manifest.json',
349+
},
350+
bridge: {
351+
// 禁用路由支持以减小 bundle 体积
352+
enableBridgeRouter: false,
353+
},
354+
}),
355+
],
356+
};
357+
```
358+
359+
:::tip 配置行为
360+
- **`enableBridgeRouter: false`**: 自动 alias`/base` 入口(不包含 react-router-dom 代码)
361+
- **`enableBridgeRouter: true`****`undefined`**: 包含路由支持(默认行为)
362+
363+
:::
364+
365+
### 何时禁用 Router
366+
367+
**禁用 router** (`enableBridgeRouter: false`) 适用于:
368+
- ✅ 应用不使用 react-router
369+
- ✅ 想要最小化 bundle 体积
370+
- ✅ 可以手动管理 basename(如果需要)
371+
372+
**保持 router 启用**(默认)适用于:
373+
- ✅ 应用使用 react-router
374+
- ✅ 需要自动 basename 注入
375+
- ✅ 需要路由上下文集成
376+
377+
### 迁移示例
378+
379+
#### 迁移前:启用 Router(默认)
380+
```tsx
381+
import { createRemoteAppComponent } from '@module-federation/bridge-react';
382+
383+
const RemoteApp = createRemoteAppComponent({
384+
loader: () => loadRemote('remote1/app'),
385+
loading: <div>Loading...</div>,
386+
fallback: ErrorBoundary,
387+
});
388+
389+
// basename 自动从路由上下文获取
390+
<RemoteApp />
391+
```
392+
393+
#### 迁移后:禁用 Router(优化)
394+
```ts title="rsbuild.config.ts"
395+
// 配置
396+
pluginModuleFederation({
397+
bridge: {
398+
enableBridgeRouter: false, // 禁用 router
399+
},
400+
})
401+
```
402+
403+
```tsx
404+
import { createRemoteAppComponent } from '@module-federation/bridge-react';
405+
406+
const RemoteApp = createRemoteAppComponent({
407+
loader: () => loadRemote('remote1/app'),
408+
loading: <div>Loading...</div>,
409+
fallback: ErrorBoundary,
410+
});
411+
412+
// 无需修改代码!插件会自动 alias 到 /base 入口
413+
<RemoteApp basename="/" /> // 如果需要,手动传递 basename
414+
```
415+
416+
:::info 工作原理
417+
当设置 `enableBridgeRouter: false` 时,Module Federation 插件会自动设置 webpack alias
418+
```
419+
'@module-federation/bridge-react' → '@module-federation/bridge-react/base'
420+
```
421+
422+
这意味着你的导入会自动解析到无路由版本,无需修改任何代码!
423+
:::

packages/bridge/bridge-react-webpack-plugin/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class ReactBridgeAliasChangerPlugin {
4141
const originalResolve = compiler.options.resolve || {};
4242
const originalAlias = originalResolve.alias || {};
4343

44-
// Update alias
45-
const updatedAlias = {
44+
// Update alias - set up router version alias
45+
const updatedAlias: Record<string, string> = {
4646
// allow `alias` can be override
4747
// [this.alias]: targetFilePath,
4848
...getBridgeRouterAlias(originalAlias['react-router-dom']),

packages/bridge/bridge-react/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
"import": "./dist/index.es.js",
2222
"require": "./dist/index.cjs.js"
2323
},
24+
"./base": {
25+
"types": "./dist/base.d.ts",
26+
"import": "./dist/base.es.js",
27+
"require": "./dist/base.cjs.js"
28+
},
2429
"./v18": {
2530
"types": "./dist/v18.d.ts",
2631
"import": "./dist/v18.es.js",
@@ -83,6 +88,9 @@
8388
".": [
8489
"./dist/index.d.ts"
8590
],
91+
"base": [
92+
"./dist/base.d.ts"
93+
],
8694
"v18": [
8795
"./dist/v18.d.ts"
8896
],
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
export { createBridgeComponent } from './provider/versions/legacy';
2+
3+
// Export router-free versions of remote component creators
4+
export {
5+
createRemoteComponent,
6+
createRemoteAppComponent,
7+
} from './remote/base-component';
8+
export type { LazyRemoteComponentInfo } from './remote/base-component';
9+
10+
// Export all lazy loading and data fetching utilities (no router dependencies)
11+
export {
12+
ERROR_TYPE,
13+
createLazyComponent,
14+
collectSSRAssets,
15+
callDataFetch,
16+
setSSREnv,
17+
autoFetchDataPlugin,
18+
CacheSize,
19+
CacheTime,
20+
configureCache,
21+
generateKey,
22+
cache,
23+
revalidateTag,
24+
clearStore,
25+
prefetch,
26+
} from './lazy';
27+
28+
export { lazyLoadComponentPlugin } from './plugins/lazy-load-component-plugin';
29+
30+
// Export all types
31+
export type { CreateRootOptions, Root } from './provider/versions/legacy';
32+
export type {
33+
ProviderParams,
34+
ProviderFnParams,
35+
RootType,
36+
DestroyParams,
37+
RenderParams,
38+
RemoteComponentParams,
39+
RenderFnParams,
40+
RemoteComponentProps,
41+
RemoteModule,
42+
} from './types';
43+
export type {
44+
DataFetchParams,
45+
NoSSRRemoteInfo,
46+
CollectSSRAssetsOptions,
47+
CreateLazyComponentOptions,
48+
CacheStatus,
49+
CacheStatsInfo,
50+
} from './lazy';

packages/bridge/bridge-react/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ export { createBridgeComponent } from './provider/versions/legacy';
66
export {
77
createRemoteComponent,
88
createRemoteAppComponent,
9-
} from './remote/create';
10-
export type { LazyRemoteComponentInfo } from './remote/create';
9+
} from './remote/router-component';
10+
export type { LazyRemoteComponentInfo } from './remote/router-component';
1111
export {
1212
ERROR_TYPE,
1313
createLazyComponent,

0 commit comments

Comments
 (0)