基于 Webpack Module Federation 的微前端配置
微前端是将一个大型应用分割成几个单独的应用的架构模式,不同于组件或者模块,微前端模块可单独部署,维护和升级。
分割的模式主要有两种:
- 根据路由分割,一个或几个页面根据功能划分分配给不同的项目团队,这是最常见的分割模式。
- 根据模块划分,比如基础的公共模块,设计系统,需要在不同的项目之间共享,可做成微前端公共模块,由于微前端是单独维护升级,对于这种公共模块划分对团队的需求比较高,需要做好发布的版本控制,和接口规范,以免独立升级之后影响到多个项目。
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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论