13

Webpack 5 モジュールのフェデレーション機能を調べていましたが、コードが機能しない理由を理解するのに苦労しています。この考え方は、標準モジュール フェデレーションの例が行うことと非常によく似ています。

app1- ホストアプリです app2- アプリ全体を公開するリモートですapp1

(app1ヘッダーと水平線をレンダリングし、その下にapp2レンダリングする必要があります)

との両方が、 との共有、シングルトン、熱心な依存関係として次のようapp1app2宣言しreactます。react-domweback.config.js

// app1 webpack.config.js
module.exports = {
  entry: path.resolve(SRC_DIR, './index.js');,
  ...
  plugins: [
    new ModuleFederationPlugin({
      name: "app1",
      remotes: {
        app2: `app2@//localhost:2002/remoteEntry.js`,
      },
      shared: { react: { singleton: true, eager: true }, "react-dom": { singleton: true, eager: true } },
    }),
    ...
  ],
};
// app2 webpack.config.js
module.exports = {
  entry: path.resolve(SRC_DIR, './index.js');,
  ...
  plugins: [
    new ModuleFederationPlugin({
      name: "app2",
      library: { type: "var", name: "app2" },
      filename: "remoteEntry.js",
      exposes: {
        "./App": "./src/App",
      },
      shared: { react: { singleton: true, eager: true }, "react-dom": { singleton: true, eager: true } },
    }),
    ...
  ],
};

App1 index.js には、次のコードがあります。

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";


ReactDOM.render(<App />, document.getElementById("root"));

App1App.jsコンポーネントは次のとおりです。

import React, { Suspense } from 'react';

const RemoteApp2 = React.lazy(() => import("app2/App"));

export default function App() {
  return (
    <div>
      <h1>App 1</h1>
      <p>Below will be some content</p>
      <hr/>
      <Suspense fallback={'Loading App 2'}>
        <RemoteApp2 />
      </Suspense>
    </div>
  );
}

しかし、アプリケーションを起動すると、次のエラーが発生します。

Uncaught Error: Shared module is not available for eager consumption: webpack/sharing/consume/default/react/react?1bb3
    at Object.__webpack_modules__.<computed> (consumes:133)
    at __webpack_require__ (bootstrap:21)
    at fn (hot module replacement:61)
    at Module../src/index.js (main.bundle.a8d89941f5dd9a37d429.js:239)
    at __webpack_require__ (bootstrap:21)
    at startup:4
    at startup:6

index.jstobootstrap.jsと inからすべてを抽出するとうまくindex.jsいきます

import('./bootstrap');

すべてがうまく機能します。

これは、作成者からの公式ドキュメントブログ投稿が、どちらの方法でも実行できるか、bootstrap.js依存関係を熱心なものとして宣言できると述べているため、私を混乱させます。

bootstrap.jsパターンなしでは機能しない理由について、ヘルプ/洞察をいただければ幸いです。

これは、私が構築していた完全な GitHub サンドボックスへのリンクです: https://github.com/vovkvlad/webpack-module-federation-sandbox/tree/master/simple

4

4 に答える 4

12

最初の回答に対するコメントを見逃す可能性がある人のために明確にするために:

最初に失敗した主な理由はremoteEntry.js、実際にホスト アプリを実行するコードの後に​​ファイルが読み込まれたことだったようです。

アプローチとタグへのbootstrap.js直接スクリプトの追加の両方の結果はまったく同じです。メインアプリのコードのに読み込まれ、解析されます。<script src="http://localhost:2002/remoteEntry.js"></script><head></head>remoteEntry.js

ブートストラップの場合、順序は次のとおりです。

  1. main_bundleロードされています
  2. メインコードがbootstrap.jsファイルに抽出されると、remoteEntry.jsロードされます
  3. bootstrap.js実際にメインアプリを実行するロードされます

ここに画像の説明を入力

Oleg Vodolazsky イベントの順序で提案されたバリアントは次のとおりです。

  1. remoteEntry.jshtmlファイルに直接追加され、webpack がremoteEntry リンクの後main_bundleに追加されるため、最初にロードされます<head></head>
  2. main_bundleロードされ、アプリケーションを実行します

ここに画像の説明を入力

そして、ブートストラップなしでハードコードされたスクリプトなしでアプリを実行しようとすると、実際にアプリを実行しようとする前に読み込まれ、エラーが発生して失敗します<head></head> main_bundleremoteEntry.jsmain_bundle

ここに画像の説明を入力

于 2021-03-15T13:28:29.917 に答える
6

これを機能させるには、リモート エントリのロード方法を変更する必要があります。

  1. app1 の構成を次のように更新しModuleFederationPluginます。webpack.config.js
...

new ModuleFederationPlugin({
    name: "app1",
    remoteType: 'var',
    remotes: {
      app2: 'app2',
    },
    shared: {
      ...packageJsonDeps,
      react: { singleton: true, eager: true, requiredVersion: packageJsonDeps.react },
      "react-dom": { singleton: true, eager: true, requiredVersion: packageJsonDeps["react-dom"] }
    },
}),

...
  1. app1の にscriptタグを追加しheadます。index.html
<script src="http://localhost:2002/remoteEntry.js"></script>

さらにハッキングでグッドルック!

アップデート:

念のため: 上記の修正を含むサンドボックス リポジトリへの PR を作成しました: https://github.com/vovkvlad/webpack-module-federation-sandbox/pull/2

于 2021-03-15T00:18:24.763 に答える
3

モジュール フェデレーションの高度な API 内で依存関係を熱心に設定できます。これにより、モジュールは非同期チャンクに配置されず、同期的に提供されます。これにより、これらの共有モジュールを最初のチャンクで使用できます。ただし、提供されたすべてのフォールバック モジュールは常にダウンロードされるため、注意してください。シェルなど、アプリケーションの 1 点でのみ提供することをお勧めします。

Webpack の Web サイトでは、非同期境界を使用することを強く推奨しています。追加のラウンドトリップを回避し、一般的にパフォーマンスを向上させるために、より大きなチャンクの初期化コードを分割します。

たとえば、エントリは次のようになります。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));

bootstrap.js ファイルを作成し、エントリの内容をそこに移動して、そのブートストラップをエントリにインポートしましょう。

index.js

+ import('./bootstrap');
- import React from 'react';
- import ReactDOM from 'react-dom';
- import App from './App';
- ReactDOM.render(<App />, document.getElementById('root'));

ブートストラップ.js

+ import React from 'react';
+ import ReactDOM from 'react-dom';
+ import App from './App';
+ ReactDOM.render(<App />, document.getElementById('root'));

この方法は機能しますが、制限や欠点がある場合があります。

ModuleFederationPlugin を介して依存関係にeager: true を設定する

webpack.config.js

// ...
new ModuleFederationPlugin({
  shared: {
    ...deps,
    react: {
      eager: true,
    },
  },
});

ソース

于 2021-08-29T08:02:50.640 に答える