Skip to content

Commit

Permalink
feat: add shareStrategy option (#2870)
Browse files Browse the repository at this point in the history
  • Loading branch information
2heal1 authored Aug 22, 2024
1 parent 14d499e commit b90fa7d
Show file tree
Hide file tree
Showing 25 changed files with 134 additions and 120 deletions.
7 changes: 7 additions & 0 deletions .changeset/tasty-hotels-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@module-federation/enhanced': patch
'@module-federation/runtime': patch
'@module-federation/sdk': patch
---

feat: add shareStrategy option
33 changes: 0 additions & 33 deletions apps/runtime-demo/3005-runtime-host/runtimePlugin.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/runtime-demo/3005-runtime-host/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = composePlugins(withNx(), withReact(), (config, context) => {
dts: {
tsConfigPath: path.resolve(__dirname, 'tsconfig.app.json'),
},
shareStrategy: 'loaded-first',
shared: {
lodash: {
singleton: true,
Expand All @@ -73,7 +74,6 @@ module.exports = composePlugins(withNx(), withReact(), (config, context) => {
requiredVersion: '^18.2.0',
},
},
runtimePlugins: [path.join(__dirname, './runtimePlugin.ts')],
}),
);
config.optimization.runtimeChunk = false;
Expand Down
33 changes: 0 additions & 33 deletions apps/runtime-demo/3006-runtime-remote/runtimePlugin.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/runtime-demo/3006-runtime-remote/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module.exports = composePlugins(
'./WebpackSvg': './src/components/WebpackSvg',
'./WebpackPng': './src/components/WebpackPng',
},
shareStrategy: 'loaded-first',
shared: {
lodash: {
singleton: true,
Expand Down Expand Up @@ -64,7 +65,6 @@ module.exports = composePlugins(
dts: {
tsConfigPath: path.resolve(__dirname, 'tsconfig.app.json'),
},
runtimePlugins: [path.join(__dirname, './runtimePlugin.ts')],
}),
);
// config.externals={
Expand Down
1 change: 1 addition & 0 deletions apps/runtime-demo/3007-runtime-remote/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module.exports = composePlugins(
requiredVersion: '^18.2.0',
},
},
shareStrategy: 'loaded-first',
dev: {
disableLiveReload: true,
},
Expand Down
5 changes: 5 additions & 0 deletions apps/website-new/docs/en/configure/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,10 @@
"type": "file",
"name": "manifest",
"label": "Manifest"
},
{
"type": "file",
"name": "shareStrategy",
"label": "shareStrategy"
}
]
11 changes: 11 additions & 0 deletions apps/website-new/docs/en/configure/shareStrategy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# shareStrategy

- Type: `'version-first' | 'loaded-first'`
- Required: No
- Default: `'version-first'`

Control the loading strategy of shared dependencies:

- `'version-first'`: Version priority, ensuring that the highest version of shared dependencies is used. After setting, all *remotes* entry files will be automatically loaded and **register** the corresponding shared dependencies to ensure that all shared dependency versions can be obtained. This strategy is recommended when there are strict version requirements.

- `'loaded-first'`: Prioritize reuse, greatly reducing redundant dependency requests. After setting, the *remotes* entry file will not be automatically loaded (it will only be loaded when needed), and the loaded shared dependencies will be reused first. This strategy is recommended when there are no strict requirements on the version and performance is required.
32 changes: 2 additions & 30 deletions apps/website-new/docs/en/guide/basic/error-catalog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,16 @@ Error: [ Federation Runtime ]:
:::

#### Solution
1. Create a shared-strategy in all hosts and remotes

```ts title="shared-strategy.ts"
``import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const sharedStrategy: () => FederationRuntimePlugin = () => ({
name: 'shared-strategy-plugin',
beforeInit(args) {
const { userOptions } = args;
const shared = userOptions.shared;
if (shared) {
Object.keys(shared).forEach((sharedKey) => {
const sharedConfigs = shared[sharedKey];
const arraySharedConfigs = Array.isArray(sharedConfigs)
? sharedConfigs
: [sharedConfigs];
arraySharedConfigs.forEach((s) => {
s.strategy = 'loaded-first';
});
});
}
return args;
},
});
export default sharedStrategy;
```

2. Add the shared-strategy to your config
Set `shareStrategy` as `'loaded-first'` host
```ts title="modern.config.js"
{
...
new ModuleFederationPlugin({
...,
runtimePlugins: ['path/to/shared-strategy.ts'],
shareStrategy: 'loaded-first',
})
}
```

3. Optional: If you are running `module-federation` in a Docker environment, make sure to adapt the following fields in your configs of all `hosts` and `remotes`:
```ts title="modern.config.js"
{
Expand Down
5 changes: 5 additions & 0 deletions apps/website-new/docs/zh/configure/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,10 @@
"type": "file",
"name": "manifest",
"label": "manifest"
},
{
"type": "file",
"name": "shareStrategy",
"label": "shareStrategy"
}
]
11 changes: 11 additions & 0 deletions apps/website-new/docs/zh/configure/shareStrategy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# shareStrategy

- 类型:`'version-first' | 'loaded-first'`
- 是否必填:否
- 默认值:`'version-first'`

控制共享依赖的加载策略:

- `'version-first'`:版本优先,确保使用最高版本的共享依赖。设置后,会自动加载所有 *remotes* 入口文件,并**注册**对应的共享依赖,确保能获取到所有的共享依赖版本。当对版本有严格要求时,推荐使用此策略。

- `'loaded-first'`:复用优先,大幅减少多余的依赖请求。设置后,不会自动加载 *remotes* 入口文件(仅在有需求时才会加载),优先复用已加载的共享依赖。当对版本没有严格要求且对性能有要求时,推荐使用此策略。
4 changes: 4 additions & 0 deletions packages/enhanced/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ The following items are exported:
- AsyncBoundaryPlugin
- HoistContainerReferencesPlugin

## Documentation

See [https://module-federation.io/guide/basic/webpack.html](https://module-federation.io/guide/basic/webpack.html) for details.

## ModuleFederationPlugin

### Configuration
Expand Down
1 change: 1 addition & 0 deletions packages/enhanced/src/lib/container/runtime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function normalizeRuntimeInitOptionsWithOutShared(
const initOptionsWithoutShared = {
name: options.name!,
remotes: remoteOptions,
shareStrategy: options.shareStrategy || 'version-first',
};

return initOptionsWithoutShared;
Expand Down
10 changes: 10 additions & 0 deletions packages/enhanced/src/schemas/container/ModuleFederationPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,10 @@ export default {
],
},
},
ShareStrategy: {
description: "Load shared strategy(defaults to 'version-first').",
enum: ['version-first', 'loaded-first'],
},
Shared: {
description:
'Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation.',
Expand Down Expand Up @@ -463,6 +467,9 @@ export default {
type: 'string',
minLength: 1,
},
shareStrategy: {
$ref: '#/definitions/ShareStrategy',
},
requiredVersion: {
description: 'Version requirement from module in share scope.',
anyOf: [
Expand Down Expand Up @@ -762,6 +769,9 @@ export default {
type: 'string',
minLength: 1,
},
shareStrategy: {
$ref: '#/definitions/ShareStrategy',
},
shared: {
$ref: '#/definitions/Shared',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

export default () => {
return `App rendered with [${React()}]`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
it('should load the component from container', () => {
return import('./App').then(({ default: App }) => {
expect(App()).toBe('App rendered with [This is react 0.1.2]');

const shareStrategy =
__webpack_require__.federation.initOptions.shareStrategy;
// name: "react", version: "0.1.2", eager: 0, singleton: 1, requiredVersion: "*", strictVersion: 0
expect(shareStrategy).toBe('version-first');
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "shared-strategy"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { SharePlugin } = require('../../../../dist/src');

module.exports = {
mode: 'development',
devtool: false,
plugins: [
new SharePlugin({
name: 'shared-strategy',
shared: {
react: {
requiredVersion: false,
singleton: true,
strictVersion: false,
version: '0.1.2',
},
},
}),
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export class NextFederationPlugin {
: { manifest: { filePath: '/static/chunks' } }),
// nextjs project needs to add config.watchOptions = ['**/node_modules/**', '**/@mf-types/**'] to prevent loop types update
dts: this._options.dts ?? false,
shareStrategy: this._options.shareStrategy ?? 'loaded-first',
};
}

Expand Down
16 changes: 0 additions & 16 deletions packages/nextjs-mf/src/plugins/container/runtimePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,7 @@ export default function (): FederationRuntimePlugin {
return mod;
},
beforeInit(args) {
const { userOptions, shareInfo } = args;
const { shared } = userOptions;
if (!globalThis.usedChunks) globalThis.usedChunks = new Set();
if (shared) {
Object.keys(shared || {}).forEach((sharedKey) => {
const rawShared = shared[sharedKey];
const arrayShared = Array.isArray(rawShared)
? rawShared
: [rawShared];
arrayShared.forEach((s) => {
if (!s.strategy) {
s.strategy = 'loaded-first';
}
});
});
}

if (
typeof __webpack_runtime_id__ === 'string' &&
!__webpack_runtime_id__.startsWith('webpack')
Expand Down
9 changes: 7 additions & 2 deletions packages/runtime/src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Shared,
RemoteEntryExports,
UserOptions,
ShareStrategy,
} from '../type';
import { FederationHost } from '../core';
import {
Expand Down Expand Up @@ -241,7 +242,7 @@ export class SharedHandler {
// eslint-disable-next-line @typescript-eslint/member-ordering
initializeSharing(
shareScopeName = DEFAULT_SCOPE,
strategy?: Shared['strategy'],
strategy?: ShareStrategy,
): Array<Promise<void>> {
const { host } = this;

Expand Down Expand Up @@ -297,7 +298,11 @@ export class SharedHandler {
}
});
});
if (strategy === 'version-first') {
// TODO: strategy==='version-first' need to be removed in the future
if (
host.options.shareStrategy === 'version-first' ||
strategy === 'version-first'
) {
host.options.remotes.forEach((remote) => {
if (remote.shareScope === shareScopeName) {
promises.push(initRemoteModule(remote.name));
Expand Down
8 changes: 6 additions & 2 deletions packages/runtime/src/type/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export type ShareArgs =
| (SharedBaseArgs & { get: SharedGetter })
| (SharedBaseArgs & { lib: () => Module })
| SharedBaseArgs;

export type ShareStrategy = 'version-first' | 'loaded-first';
export type Shared = {
version: string;
get: SharedGetter;
Expand All @@ -81,7 +81,10 @@ export type Shared = {
loading?: null | Promise<any>;
// compatibility with previous shared
eager?: boolean;
strategy: 'version-first' | 'loaded-first';
/**
* @deprecated set in initOptions.shareStrategy instead
*/
strategy: ShareStrategy;
};

export type ShareScopeMap = {
Expand All @@ -108,6 +111,7 @@ export interface Options {
shared: ShareInfos;
plugins: Array<FederationRuntimePlugin>;
inBrowser: boolean;
shareStrategy?: ShareStrategy;
}

export type UserOptions = Omit<
Expand Down
Loading

0 comments on commit b90fa7d

Please sign in to comment.