Webpack module联合不适用于急切的共享库

IT技术 javascript reactjs webpack webpack-5 webpack-module-federation
2021-05-03 04:16:24

我正在研究 Webpack 5 module联合功能,但在理解为什么我的代码不起作用时遇到了一些麻烦。这个想法与标准module联合示例所做的非常相似:

app1- 是主机应用程序 app2- 是一个远程暴露整个应用程序app1

app1呈现标题和水平线,app2在其下方呈现)

双方app1app2声明reactreact-dom他们的共享,单,在急切的依赖weback.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.jsbootstrap.jsindex.js会做

import('./bootstrap');

一切正常。

这让我感到困惑,因为创作者的官方文档博客文章指出,您可以采用任何一种bootstrap.js方式,也可以将依赖项声明为急切的依赖项。

感谢您提供有关为什么它在没有bootstrap.js模式的情况下不起作用的任何帮助/见解

这是我正在构建的完整 GitHub 沙箱的链接:https : //github.com/vovkvlad/webpack-module-federation-sandbox/tree/master/simple

3个回答

只是为了让那些可能会错过对初始答案的评论的人清楚:

最初失败的主要原因似乎是该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.js首先加载,因为它被直接添加到html文件中,并且 webpackmain_bundle被附加到<head></head>remoteEntry 链接之后
  2. main_bundle 已加载并运行应用程序

在此处输入图片说明

如果只是尝试在没有引导程序的情况下运行应用程序并且在<head></head> main_bundle加载之前没有硬编码脚本remoteEntry.jsmain_bundle尝试实际运行应用程序时,它会失败并显示错误:

在此处输入图片说明

为了使其工作,您需要更改加载远程条目的方式。

  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. 添加script标签到head你的index.html在APP1:
<script src="http://localhost:2002/remoteEntry.js"></script>

进一步的黑客攻击好看!

更新:

只是为了它:我已经使用上述修复程序为您的沙箱存储库创建了一个 PR:https : //github.com/vovkvlad/webpack-module-federation-sandbox/pull/2

您可以在 Module Federation 的高级 API 中将依赖项设置为eager,它不会将module放在异步块中,而是同步提供它们。这允许我们在初始块中使用这些共享module。但要小心,因为所有提供的和后备module将始终被下载。建议仅在应用程序的某一点提供它,例如外壳。

Webpack 的网站强烈推荐使用异步边界。它将拆分更大块的初始化代码,以避免任何额外的往返并提高总体性能。

例如,您的条目如下所示:

index.js

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

让我们创建 bootstrap.js 文件并将条目的内容移动到其中,然后将该引导程序导入到条目中:

索引.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,
    },
  },
});

来源