基于 Webpack Module Federation 的微前端配置

发布于 2023-12-15 21:25:16 字数 4718 浏览 27 评论 0

微前端是将一个大型应用分割成几个单独的应用的架构模式,不同于组件或者模块,微前端模块可单独部署,维护和升级。

分割的模式主要有两种:

  1. 根据路由分割,一个或几个页面根据功能划分分配给不同的项目团队,这是最常见的分割模式。
  2. 根据模块划分,比如基础的公共模块,设计系统,需要在不同的项目之间共享,可做成微前端公共模块,由于微前端是单独维护升级,对于这种公共模块划分对团队的需求比较高,需要做好发布的版本控制,和接口规范,以免独立升级之后影响到多个项目。

Module Federation 的导入路径:

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

假设有两个微前端项目,一个是 shell,一个是 remote home page :

Remote Home Page 的 Webpack 配置:

  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html',
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin(),
    new ModuleFederationPlugin({
      name: 'homepage', // 该名称必须与入口名称相匹配
      filename: "remoteEntry.js",
      exposes: {
        './App': './src/App',  // 导出组件的映射表
      },
      /**
       * 定义共享库,较少组件编译和加载的体积,当作为其他模块的依赖项时,最好设置,当作为独立应用调试的时候
       * 设置这个选项会报错,可分别根据不同环境来编译不同版本。
       */
      //shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
    }),
  ],
  resolve: {
    extensions: [
      '.tsx',
      '.ts',
      '.js'
    ],
    alias: {
      'react-dom': '@hot-loader/react-dom'
    }
  },
  /**
   * 作为其他应用的依赖项时,optimization 部分不要设置
   */
  // optimization: {
  //   runtimeChunk: 'single',
  //   splitChunks: {
  //     cacheGroups: {
  //       vendor: {
  //         test: /[\\/]node_modules[\\/]/,
  //         name: 'vendors',
  //         chunks: 'all'
  //       }
  //     }
  //   }
  // }

shell 应用的 webpack 配置:

  plugins: [
    new ModuleFederationPlugin({
      name: "shell",  // 定义本应用的名称
      remotes: {
        // 注意这里的名称需要一致
        homepage: "homepage@http://localhost:8082/remoteEntry.js",
      },
      //shared: {react: {singleton: true}, "react-dom": {singleton: true}},
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin(),
    new HtmlWebpackPlugin({
      template: 'src/index.html',
    }),
  ],
  resolve: {
    extensions: [
      '.tsx',
      '.ts',
      '.js'
    ],
    alias: {
      'react-dom': '@hot-loader/react-dom'
    }
  },
  // 本身这个 shell 应用是最外部的容器,这里可定义 optimization 设置
  optimization: {
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }

在 shell 应用中引入微前端组件:

import * as React from 'react';
import { hot } from "react-hot-loader/root";
import {Suspense} from "react";
// @ts-ignore   注意这里的名称
const RemoteApp = React.lazy(() => import("homepage/App"));

import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link
} from "react-router-dom";

interface Props {
   name:
    string
}

function RemoteHomePage () {
    return (
        <Suspense fallback={"loading..."}>
            <RemoteApp/>
        </Suspense>
    );
}

function HomePage() {
    return (
        <>
            <h3>Home page</h3>
        </>
    );
}

class App extends React.Component<Props> {
    render() {
        const { name } = this.props;
        return (
            <>
                <h1>
                    Hello {name}
                </h1>
                <Router>
                    <div>
                        <ul>
                            <li>
                                <Link to="/">Home</Link>
                            </li>
                            <li>
                                <Link to="/remote">Remote Home page</Link>
                            </li>
                        </ul>

                        <Switch>
                            <Route path="/remote" component={RemoteHomePage} />
                            {/* If none of the previous routes render anything,
                                this route acts as a fallback.

                                Important: A route with path="/" will *always* match
                                the URL because all URLs begin with a /. So that's
                                why we put this one last of all */}
                            <Route path="/" component={HomePage} />
                        </Switch>
                    </div>
                </Router>
            </>
        );
    }
}

export default hot(App);

shared 选项可配置:

      shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true } },

这样既可以作为独立的应用来调试,也可以作为其他应用的组件,具体的逻辑需要查看文档,singleton 选项可能不会将共享库编译进去,导致独立运行时缺乏组件而报错,而 eager 选项会优先加载依赖库,然和它是否会去检测,只在没有加载依赖的时候才去加载需要查看相关文档。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

他不在意

暂无简介

文章
评论
26 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文