微前端中具有相同导入的组件

Components with same import in micro-frondends

我正在使用来自 webpack 5 的 ModuleFederationPlugin ModuleFederationPlugin 构建基于微前端的 React 应用程序。

我有两个单独的项目(App1 和 App2),它们公开组件,这些组件在其他项目 (AppShell) 中使用。

App1 结构:

components/Button.tsx
App1.tsx

App2 结构:

components/Button.tsx
App2.tsx

两个项目都包含组件 Button.tsx,这意味着在两个项目中该组件具有相同的导出。此组件用于 App1.tsx (App2.tsx).

现在如果我想在 AppShell 中使用 App1 和 App2,我总是会看到来自 App1 的按钮:

App1 webpack配置(App2类似):

const HtmlWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");


module.exports = {
    entry: "./src/index.tsx",
    output: {
        filename: "test-app1.[contenthash].js",
    },

    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.json'],
    },
    module: {
        rules: [
            {
                test: /\.(ts|js)x?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
            },
        ],
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({template: './public/index.html'}),
        new ModuleFederationPlugin({
            name: "remoteApp",
            library: { type: "var", name: "remoteApp" },
            filename: "remoteEntry.js",
            exposes: {
                "./MyApp": "./src/MyApp",
            },
            shared: ["react", "react-dom", "@material-ui/core"],
        })
    ]};

AppShell webpack 配置:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");


module.exports = {
    entry: "./src/index.tsx",
    output: {
        filename: "app_shell.[contenthash].js",
        path: path.resolve(__dirname, "dist"),
        publicPath: ""
    },

    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.json'],
    },
    module: {
        rules: [
            {
                test: /\.(ts|js)x?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
            },
            {
                test: /bootstrap\.js$/,
                loader: "bundle-loader",
                options: {
                    lazy: true,
                },
            },
        ],
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({template: './public/index.html'}),
        new ModuleFederationPlugin({
            name: "app_shell",
            remotes: {
                "remoteApp-mfe": "remoteApp@http://localhost:8001/remoteEntry.js",
                "remoteApp2-mfe": "remoteApp2@http://localhost:8002/remoteEntry.js",
            },
            shared: {react: {singleton: true}, "react-dom": {singleton: true}, "@material-ui/core": {singleton: true}},
        }),

    ]};

AppShell 中的组件使用:

import React from "react";

export const AppShell: React.FC = () => {
  // @ts-ignore
  const App1 = React.lazy(() => import("remoteApp-mfe/MyApp"));
  // @ts-ignore
  const App2 = React.lazy(() => import("remoteApp2-mfe/MyApp2"));

  return (
    <>
      <h1>App Shell</h1>
      <React.Suspense fallback="Loading...">
        <App1 />
      </React.Suspense>
      <React.Suspense fallback="Loading...">
        <App2 />
      </React.Suspense>
    </>
  );
};

您可以下载测试应用程序here

请问您知道如何解决这个问题吗? (我知道我可以重构应用程序,但这不是我正在寻找的解决方案;))。 谢谢。

错误在于您的各个应用程序的包名称。您的应用程序文件夹中的每个 package.json 都具有名称 "test":

app-shell/
  public/
  src/
  package.json -> package name = "test"
  ...
test-app-01/
  public/
  src/
  package.json -> package name = "test"
  ...
test-app-02/
  public/
  src/
  package.json -> package name = "test"
  ...

当您的应用程序在客户端拼接在一起时,所有 3 个包中的所有应用程序代码都会合并到同一范围内 "test"。现在出现问题是因为应用程序具有部分相同的文件夹和文件结构。

test-app-01/
  public/
  src/
    components/
      MyButton.tsx
    // other code
  ...
test-app-02/
  public/
  src/
    components/
      MyButton.tsx
    // other code
  ...

由于在同一范围内在客户端合并 "test",按钮代码被两个 test-apps 中的任何一个覆盖,如下所示:

test/
  src/
    components/
      MyButton.tsx // MyButton.tsx from test-app-01 or test-app-02 gets overridden
    // other code of app-shell, test-app-01 and test-app-02.

解决方案是在 package.json 中为所有应用程序提供单独的包名称。

将 ModuleFederationPlugin 导入更改为

const { ModuleFederationPlugin } = require('webpack').container;