Module Federation进阶构建灵活的微前端架构前言大家好我是cannonmonster01今天我们来深入探讨Module Federation这个强大的微前端技术。想象一下你在一个大型购物中心里每个店铺都是独立经营的但它们共享同一个商场的基础设施。Module Federation就像是这个商场它让多个独立的应用可以共享代码和资源。如果你想要构建灵活、可扩展的微前端架构Module Federation绝对值得一试Module Federation核心概念什么是Module FederationModule Federation是Webpack 5引入的一项功能它允许不同的构建产物bundles之间共享模块。核心概念概念描述Host主应用消费其他模块Remote远程应用提供模块Shared共享的依赖Exposes暴露给其他应用的模块基本配置// webpack.config.js (Host) const ModuleFederationPlugin require(webpack/lib/container/ModuleFederationPlugin); module.exports { plugins: [ new ModuleFederationPlugin({ name: host, remotes: { app1: app1http://localhost:3001/remoteEntry.js, app2: app2http://localhost:3002/remoteEntry.js, }, shared: { react: { singleton: true }, react-dom: { singleton: true }, }, }), ], };// webpack.config.js (Remote) const ModuleFederationPlugin require(webpack/lib/container/ModuleFederationPlugin); module.exports { plugins: [ new ModuleFederationPlugin({ name: app1, filename: remoteEntry.js, exposes: { ./Button: ./src/components/Button, ./utils: ./src/utils, }, shared: { react: { singleton: true }, react-dom: { singleton: true }, }, }), ], };Module Federation实战实战1创建Host应用// webpack.config.js (Host) const path require(path); const HtmlWebpackPlugin require(html-webpack-plugin); const ModuleFederationPlugin require(webpack/lib/container/ModuleFederationPlugin); module.exports { entry: ./src/index.js, output: { filename: [name].[contenthash].js, path: path.resolve(__dirname, dist), publicPath: http://localhost:3000/, }, plugins: [ new HtmlWebpackPlugin({ template: ./public/index.html, }), new ModuleFederationPlugin({ name: host, filename: remoteEntry.js, remotes: { authApp: authApphttp://localhost:3001/remoteEntry.js, dashboardApp: dashboardApphttp://localhost:3002/remoteEntry.js, }, shared: { react: { singleton: true, requiredVersion: ^18.0.0, }, react-dom: { singleton: true, requiredVersion: ^18.0.0, }, react-router-dom: { singleton: true, requiredVersion: ^6.0.0, }, }, }), ], resolve: { extensions: [.js, .jsx], }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: babel-loader, }, ], }, };// src/App.jsx import React, { Suspense, lazy } from react; import { BrowserRouter, Routes, Route } from react-router-dom; const Login lazy(() import(authApp/Login)); const Dashboard lazy(() import(dashboardApp/Dashboard)); function App() { return ( BrowserRouter Suspense fallback{divLoading.../div} Routes Route path/login element{Login /} / Route path/dashboard element{Dashboard /} / /Routes /Suspense /BrowserRouter ); } export default App;实战2创建Remote应用// authApp/webpack.config.js const path require(path); const HtmlWebpackPlugin require(html-webpack-plugin); const ModuleFederationPlugin require(webpack/lib/container/ModuleFederationPlugin); module.exports { entry: ./src/index.js, output: { filename: [name].[contenthash].js, path: path.resolve(__dirname, dist), publicPath: http://localhost:3001/, }, plugins: [ new HtmlWebpackPlugin({ template: ./public/index.html, }), new ModuleFederationPlugin({ name: authApp, filename: remoteEntry.js, exposes: { ./Login: ./src/components/Login, ./Signup: ./src/components/Signup, ./AuthProvider: ./src/providers/AuthProvider, }, shared: { react: { singleton: true }, react-dom: { singleton: true }, react-router-dom: { singleton: true }, }, }), ], resolve: { extensions: [.js, .jsx], }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: babel-loader, }, ], }, };// authApp/src/components/Login.jsx import React, { useState } from react; function Login() { const [email, setEmail] useState(); const [password, setPassword] useState(); const handleSubmit (e) { e.preventDefault(); console.log(Login with:, { email, password }); }; return ( div style{{ maxWidth: 400px, margin: 0 auto, padding: 20px }} h2Login/h2 form onSubmit{handleSubmit} div labelEmail:/label input typeemail value{email} onChange{(e) setEmail(e.target.value)} required / /div div labelPassword:/label input typepassword value{password} onChange{(e) setPassword(e.target.value)} required / /div button typesubmitLogin/button /form /div ); } export default Login;实战3动态加载Remote模块// src/App.jsx import React, { useState, Suspense } from react; function App() { const [moduleName, setModuleName] useState(null); const [error, setError] useState(null); const loadModule async (name) { try { setError(null); setModuleName(name); } catch (err) { setError(Failed to load module); } }; const RemoteModule moduleName ? React.lazy(() import(${moduleName}/MainComponent)) : null; return ( div button onClick{() loadModule(app1)}Load App1/button button onClick{() loadModule(app2)}Load App2/button {error div style{{ color: red }}{error}/div} Suspense fallback{divLoading.../div} {RemoteModule RemoteModule /} /Suspense /div ); } export default App;实战4共享状态管理// shared/store.js import { createStore, applyMiddleware } from redux; import thunk from redux-thunk; const initialState { user: null, isAuthenticated: false, }; function reducer(state initialState, action) { switch (action.type) { case LOGIN: return { ...state, user: action.payload, isAuthenticated: true }; case LOGOUT: return { ...state, user: null, isAuthenticated: false }; default: return state; } } export const store createStore(reducer, applyMiddleware(thunk));// host/src/index.js import React from react; import ReactDOM from react-dom; import { Provider } from react-redux; import { store } from shared/store; import App from ./App; ReactDOM.render( Provider store{store} App / /Provider, document.getElementById(root) );// remote/src/components/Login.jsx import React, { useState } from react; import { useDispatch } from react-redux; function Login() { const [email, setEmail] useState(); const dispatch useDispatch(); const handleLogin () { dispatch({ type: LOGIN, payload: { email }, }); }; return ( div input typeemail value{email} onChange{(e) setEmail(e.target.value)} / button onClick{handleLogin}Login/button /div ); } export default Login;Module Federation最佳实践1. 版本管理shared: { react: { singleton: true, requiredVersion: ^18.0.0, version: 18.2.0, }, },2. 错误边界import React, { Suspense } from react; function ErrorBoundary({ fallback, children }) { const [hasError, setHasError] React.useState(false); if (hasError) { return fallback; } return ( React.ErrorBoundary onError{() setHasError(true)} {children} /React.ErrorBoundary ); } function App() { const RemoteComponent React.lazy(() import(remote/Component)); return ( ErrorBoundary fallback{divFailed to load component/div} Suspense fallback{divLoading.../div} RemoteComponent / /Suspense /ErrorBoundary ); }3. 动态Remote配置// src/config/remotes.js export const remotes { development: { app1: app1http://localhost:3001/remoteEntry.js, app2: app2http://localhost:3002/remoteEntry.js, }, production: { app1: app1https://cdn.example.com/app1/remoteEntry.js, app2: app2https://cdn.example.com/app2/remoteEntry.js, }, }; export const getRemoteConfig () { const env process.env.NODE_ENV || development; return remotes[env]; };4. 性能优化shared: { react: { singleton: true, eager: true, // 立即加载避免重复加载 }, },Module Federation与其他微前端方案对比特性Module FederationSingle SPAqiankun构建工具Webpack 5任意任意共享依赖原生支持需要手动配置需要手动配置隔离性良好良好良好学习曲线中等低低生态系统较小较大较大常见问题解答Q1Module Federation需要Webpack 5吗A1是的Module Federation是Webpack 5引入的功能。Q2如何处理共享依赖的版本冲突A2使用requiredVersion和singleton配置来确保只有一个版本的依赖被加载。Q3Module Federation支持SSR吗A3支持但需要额外配置。可以使用module-federation/nextjs-mf等工具。Q4如何在生产环境部署Module Federation应用A4需要确保所有Remote应用的remoteEntry.js可以被Host访问可以使用CDN来托管这些文件。总结Module Federation是一个强大的微前端架构方案它让多个独立的应用可以无缝地共享代码和资源。通过合理配置我们可以构建出灵活、可扩展的微前端系统。关注我每天分享更多前端干货如果觉得这篇文章对你有帮助请点赞、收藏、转发三连支持一下