玩点不一样的:使用React中的模块联邦实现微前端

前言

本文将使用微前端架构和模块联邦构建可扩展和高效的Web应用程序的概述,并提供了一个简易教程,教你如何使用React中的模块联邦构建微前端。一步一步搭建一个ikun之家。

什么是微前端?

微前端(Micro Frontends)是一种前端架构模式,类似于微服务架构,旨在将大型前端应用拆分成更小、独立的模块,以便不同的团队可以独立开发、测试、部署和扩展各自的模块。每个模块都代表应用的一个功能或页面,它们可以独立开发和部署,同时能够在运行时无缝地集成在一起,形成一个完整的用户界面。

微前端的核心思想是将前端应用拆分成更小的部分,这些部分可以独立构建、部署和维护。每个部分被称为微前端,它可以是一个独立的代码库,使用不同的技术栈、框架甚至语言来开发。每个微前端可以有自己的开发团队,有自己的开发流程和部署策略。

什么是模块联邦?

模块联邦(Module Federation)是一种软件架构模式,旨在解决在复杂的分布式系统中,不同模块(或微服务)之间的依赖管理和共享问题。它是由Webpack社区推动和支持的。

在传统的前端应用中,通常使用Webpack等构建工具将整个应用打包成一个或多个bundle文件,然后在浏览器中加载。但是随着应用规模的增大和团队的不断扩展,将整个应用打包成单一的bundle可能会导致文件过大、加载时间长、更新和部署不便等问题。模块联邦的目标就是将这些大型应用拆分成多个独立的模块,使得不同的模块可以独立开发、构建和部署,并且能够在运行时动态地在浏览器中加载和共享这些模块。

它是如何工作的?

模块联邦是一种系统,它允许您在另一个应用程序中使用应用程序的部分,而无需导入整个应用程序。这意味着您可以将应用程序拆分为较小的模块,从而更容易管理和更新。

模块联邦的好处

模块联邦作为一种前端架构模式,带来了许多好处,特别是在处理大型、复杂的分布式系统中。以下是模块联邦的一些主要优势:

  1. 模块化开发: 模块联邦鼓励将应用拆分成小块模块,每个模块负责一个特定的功能或特性。这样的模块化开发风格有助于提高代码的可维护性、可测试性和可重用性。

  2. 独立开发和部署: 不同的模块可以由不同的团队独立开发和部署,从而减少了团队之间的依赖和协调成本。每个模块的更新和发布都不会影响其他模块,实现了更灵活的开发流程。

  3. 减少打包体积: 传统的单一bundle打包方式可能导致文件过大,增加加载时间。模块联邦可以将共享的依赖项从模块中提取出来,在运行时动态加载,从而减少每个模块的打包体积。

  4. 共享依赖: 模块联邦确保共享的依赖只会被加载和初始化一次,这样可以减少代码冗余和资源浪费,提高应用的性能。

  5. 团队协作: 模块联邦使得不同团队能够专注于各自的领域,而不必担心与其他团队的冲突。每个团队可以独立开发和维护自己的模块,降低了团队之间的合作难度。

  6. 远程模块: 可以将模块部署在不同的服务中,甚至由不同的团队维护。这种灵活性有助于更好地组织代码库,划分边界和责任。

  7. 动态加载: 模块联邦支持在运行时动态加载模块,这意味着只有在需要时才会加载模块,从而减少初始加载时间。

  8. 适用于复杂应用: 模块联邦适用于各种规模的应用,从小型应用到大型、复杂的分布式系统,都可以受益于模块联邦的架构模式。

  9. 可维护性增强: 将应用拆分成模块可以降低代码库的复杂性,使得问题排查和维护更加容易。

总的来说,模块联邦通过解决依赖管理、共享依赖、独立开发等问题,为构建现代前端应用提供了一种强大的架构模式。随着前端应用的不断发展,模块联邦的优势将继续显现,并在应对复杂性和可维护性方面发挥重要作用。

实践

这是一个使用模块联邦构建微前端的栗子。我们将构建两个应用程序:主页应用和头部应用,共同组成一个ikun之家

你可以按照以下步骤进行操作:

1.创建应用程序

使用 create-react-app 或你喜欢的方法分别创建两个新的React应用程序:

cmd 复制代码
npx create-react-app ikun-home
 
npx create-react-app ikun-header

2.安装 Webpack 5

在每个项目程序中,安装webpack 5及其相关依赖项,你可以使用npmyarn

cmd 复制代码
#NPM
npm install --save-dev webpack webpack-cli html-webpack-plugin webpack-dev-server babel-loader css-loader
cmd 复制代码
#Yarn
yarn add -D webpack webpack-cli html-webpack-plugin webpack-dev-server babel-loader css-loader

3.自定义首页

自定义你的ikun-home,例如,在ikun-home/App.js中:

jsx 复制代码
import React from 'react'; // Must be imported for webpack to work
import './App.css';

function App() {
  return (
    <div className="App">
      <img className='App-logo' src="https://sns-img-hw.xhscdn.com/47eb6e4a-0b8e-a0e7-9778-7c199931ef12?imageView2/2/w/1920/format/webp|imageMogr2/strip" alt="img"/>
    </div>
  );
}

export default App;

运行你的程序,不出意外的话你应该会看到下面这个页面:

jsx 复制代码
cd ./ikun-home && yarn && yarn start

4.自定义ikun-header项目

自定义你的ikun-header项目例如在ikun-header/App.js中:

jsx 复制代码
import React from 'react'; // Must be imported for webpack to work
import './App.css';

function App() {
  return (
    <div className="HeaderApp">
      <span>可乐要加冰,爱坤要用心。</span>
    </div>
  );
}

export default App;

运行你的程序,不出意外应该长这样:

js 复制代码
cd ./ikun-header && yarn && yarn start

5.Webpack 配置

在ikun-header/和ikun-home/的根目录下分别创建webpack.config.js文件。

jsx 复制代码
//ikun-home/webpack.config.js  
const HtmlWebpackPlugin = require("html-webpack-plugin");  
  
module.exports = {  
    entry: "./src/index",  
    mode: "development",  
    devServer: {  
        port: 3000, // port 3001 for ikun-header  
    },  
    module: {  
        rules: [  
            {  
                test: /\.(js|jsx)?$/,  
                exclude: /node_modules/,  
                use: [  
                    {  
                        loader: "babel-loader",  
                        options: {  
                            presets: ["@babel/preset-env", "@babel/preset-react"],  
                        },  
                    },  
                ],  
            },  
            {  
                test: /\.css$/i,  
                use: ["style-loader", "css-loader"],  
            },  
        ],  
    },  
    plugins: [  
        new HtmlWebpackPlugin({  
            template: "./public/index.html",  
        }),  
    ],  
    resolve: {  
        extensions: [".js", ".jsx"],  
    },  
    target: "web",  
};

分别修改/src/index.js文件:

jsx 复制代码
import React from 'react';  
import ReactDOM from 'react-dom/client';  
import './index.css';  
import App from './App';  
  
const root = ReactDOM.createRoot(document.getElementById('app'));  
root.render(  
    <React.StrictMode>  
    <App />  
    </React.StrictMode>  
);

分别修改两个项目public/index.html文件:

html 复制代码
<!DOCTYPE html>  
<html lang="en">  
<head>  
<meta charset="UTF-8" />  
<meta http-equiv="X-UA-Compatible" content="IE=edge" />  
<meta name="viewport" content="width=device-width, initial-scale=1.0" />  
<title>ikun之家,小黑子退!退!退!</title>  
</head>  
<body>  
<div id="app"></div>  
  
<script src="main.js"></script>  
</body>  
</html>

分别将package.json中的启动脚本更改为我们的webpack配置:

json 复制代码
"scripts": {  
"start": "webpack serve",  
"build": "webpack --mode production",  
},

分别运行我们的两个应用:

cmd 复制代码
cd ikun-home && yarn start  
cd ikun-header && yarn start

5.模块联邦配置

首先,我们需要添加一个名为entry.js的文件作为我们每个应用程序的入口。

我们需要这个额外的间接层,因为它允许Webpack加载渲染远程应用所需的所有导入项。

在两个项目下创建 src/entry.js文件:

js 复制代码
//entry.js
import('./index.js')

再次修改我们两个项目中的webpack.config.js

js 复制代码
module.exports = {  
  entry: "./src/entry.js",  
  //...  
}

6.为模块联邦暴露头部

现在我们需要暴露Header供ikun-home使用,在我们的ikun-header/webpack.config.js中:

js 复制代码
// ikun-header/webpack.config.js  
const HtmlWebpackPlugin = require("html-webpack-plugin");  
// import ModuleFederationPlugin from webpack  
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");  
// import dependencies from package.json, which includes react and react-dom  
const { dependencies } = require("./package.json");
js 复制代码
module.exports = {  
    //...  
    plugins: [  
        //...  
        new ModuleFederationPlugin({  
            name: "HeaderApp", // This application named 'HeaderApp'  
            filename: "remoteEntry.js", // output a js file  
            exposes: { // which exposes  
                "./Header": "./src/App", // a module 'Header' from './src/App'  
            },  
            shared: { // and shared  
                ...dependencies, // some other dependencies  
                react: { // react  
                    singleton: true,  
                    requiredVersion: dependencies["react"],  
                },  
                "react-dom": { // react-dom  
                    singleton: true,  
                    requiredVersion: dependencies["react-dom"],  
                },  
            },  
        }),  
    ],  
};

再次启动ikun-header,导航到http://localhost:3001/remoteEntry.js。这是暴露的所有模块的内容。

7.在主页项目中添加模块联邦功能

现在我们需要将ModuleFederationPlugin添加到ikun-home/webpack.config.js中:

js 复制代码
// ikun-home/webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
// import ModuleFederationPlugin from webpack
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
// import dependencies from package.json, which includes react and react-dom
const { dependencies } = require("./package.json");

module.exports = {
    entry: "./src/entry.js",  //注意这里
    mode: "development",
    devServer: {
        port: 3000,
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)?$/,
                exclude: /node_modules/,
                use: [
                    {
                    loader: "babel-loader",
                    options: {
                        presets: ["@babel/preset-env", "@babel/preset-react"],
                    },
                    },
                ],
            },
            {
                test: /\.css$/i,
                use: ["style-loader", "css-loader"],
            },
        ],
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./public/index.html",
        }),
        new ModuleFederationPlugin({
            name: "HomeApp",  // This application named 'HomeApp'
            // This is where we define the federated modules that we want to consume in this app. 
            // Note that we specify "Header" as the internal name 
            // so that we can load the components using import("Header/"). 
            // We also define the location where the remote's module definition is hosted: 
            // Header@[http://localhost:3001/remoteEntry.js]. 
            // This URL provides three important pieces of information: the module's name is "Header", it is hosted on "localhost:3001", 
            // and its module definition is "remoteEntry.js".
            remotes: { 
                "HeaderApp": "HeaderApp@http://localhost:3001/remoteEntry.js",            
            },
            shared: {  // and shared
                ...dependencies,  // other dependencies
                react: { // react
                    singleton: true,
                    requiredVersion: dependencies["react"],
                },
                "react-dom": { // react-dom
                    singleton: true,
                    requiredVersion: dependencies["react-dom"],
                },
            },
        }),
    ],
    resolve: {
        extensions: [".js", ".jsx"],
    },
    target: "web",
};

修改ikun-home/src/App.js,使用远程程序中的Header组件。

jsx 复制代码
import React, { lazy, Suspense } from 'react'; // Must be imported for webpack to work
import './App.css';

const Header = lazy(() => import('HeaderApp/Header'));


function App() {
  return (
    <div className="App">
      <Suspense fallback={<div>Loading Header...</div>}>
        <Header />
      </Suspense>
      <img className='App-logo' src="https://sns-img-hw.xhscdn.com/47eb6e4a-0b8e-a0e7-9778-7c199931ef12?imageView2/2/w/1920/format/webp|imageMogr2/strip" alt="img"/>
    </div>
  );
}

export default App;

分别启动ikun-home 和ikun-deader 项目:

cmd 复制代码
cd ikun-home && yarn start  
cd ikun-header && yarn start

完成!

打开http://localhost:3000, 不出意外的话你看到的应该和我下面的一样!

恭喜你!成功地创建了一个带有标题的模块联合应用程序。

结论

微前端架构和模块联邦是构建可扩展和高效的Web应用程序的强大工具。通过将庞大的单体前端拆分为更小、更易管理的部分,团队可以更高效、更独立地工作。而模块联邦则允许开发人员在应用程序之间共享代码,促进协作,减少重复劳动。通过按照本文中概述的步骤,可以在React中构建自己的微前端并利用这些强大的工具。

相关推荐
JUNAI_Strive_ving12 分钟前
番茄小说逆向爬取
javascript·python
看到请催我学习21 分钟前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
twins352041 分钟前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky1 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
哪 吒1 小时前
华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)
javascript·python·华为od
安冬的码畜日常1 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n02 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
Q_w77422 小时前
一个真实可用的登录界面!
javascript·mysql·php·html5·网站登录
昨天;明天。今天。2 小时前
案例-任务清单
前端·javascript·css