likes
comments
collection
share

Webpack5模块联邦微前端方案实践

作者站长头像
站长
· 阅读数 13

1.参考资料

2.Webpack5模块联邦

使用的webpack版本:5.76.2,webpack-cli版本:5.0.1,webapck-dev-server:4.13.1,使用的react版本:18.2.0。

webpack5:配置

new ModuleFederationPlugin(moduleSetting)

  • 1.Module Federation(模块联邦):可以使JavaScript应用可以动态运行另一个JavaScript应用中的代码,同时可以共享依赖

3.demo探索

1.先新建3个工程,主应用工程,嵌入的子应用工程app1app2

2.主应用配置

先搭建一个react项目,这里我不使用react的官方脚手架搭建

(1)先创建一个名叫主应用的文件夹。

(2)初始化 NPM 项目

npm init

这将创建一个 package.json 文件。

(3)安装 React 和 ReactDOM:

npm install react react-dom

(4)安装 Webpack、Babel 和相关依赖:

npm install --save-dev webpack webpack-cli webpack-dev-server babel-loader @babel/core @babel/preset-env @babel/preset-react html-webpack-plugin

(5)在项目根目录下创建一个 webpack.config.js 文件,配置如下:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'bundle.js',
    },
    devServer: {
        static: {
            directory: './dist',
        },
        port: 3000,
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env', '@babel/preset-react'],
                    },
                },
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
            },
        ],
    },
    plugins: [
        new ModuleFederationPlugin({
            name: 'mainApp',
            remotes: {
                microApp1: 'microApp1@http://localhost:3002/remoteEntry.js',
                // microApp2: 'microApp2@http://localhost:3003/remoteEntry.js',
            },
        }),
        new HtmlWebpackPlugin({
            template: './public/index.html',
        }),
    ],
};

(6)在项目根目录下创建一个 .babelrc 文件,配置如下:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

(7)创建项目的源代码结构:

在项目根目录下创建以下文件和文件夹:

mkdir public src
touch public/index.html src/index.js src/App.js

(8)编辑 public/index.html 文件,添加以下内容:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>主应用</title>
</head>

<body>
    <div id="root"></div>
</body>

</html>

(9)编辑 src/index.js 文件,添加以下内容:

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const rootElement = document.getElementById('root');
createRoot(rootElement).render(<App />);

(10)编辑 src/App.js 文件,添加以下内容:

import React, { useState, lazy, Suspense } from 'react';
import './App.css';

const RemoteApp1 = lazy(() => import('microApp1/app'));
// const RemoteApp2 = lazy(() => import('App2/RemoteApp2'));

function App() {
    const [activeApp, setActiveApp] = useState(null);

    const handleClick = (app) => {
        setActiveApp(app);
    };

    return (
        <div>
            <nav>
                <ul>
                    <li><span onClick={() => handleClick('App1')}>子应用1</span></li>
                    <li><span>子应用2</span></li>
                </ul>
            </nav>
            <div className="app-container">
                <Suspense fallback={<div>Loading...</div>}>
                    {activeApp === 'App1' && <RemoteApp1 />}
                    {/* {activeApp === 'App2' && <RemoteApp2 />} */}
                </Suspense>
            </div>
        </div>
    );
}

export default App;

(11)新建一个 src/App.css 文件,添加以下内容:

nav {
  display: flex;
  justify-content: center;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
}

li {
  margin: 0 10px;
  height: 24px;
  background: #ff5c35;
  border-radius: 5px;
}

span {
  color: #333;
  text-decoration: none;
  font-size: 18px;
  padding: 10px;
  border-bottom: 2px solid transparent;
  transition: all 0.3s ease;
  cursor: pointer;
}

span:hover {
  border-bottom: 2px solid #333;
}

(12)更新 package.json 文件,添加以下脚本:

"scripts": {
  "start": "webpack serve --mode development --open",
  "build": "webpack --mode production"
}

(13)启动开发服务器:

npm start

启动起来在浏览器上看见子应用1和子应用2的按钮那么项目就搭建成功了。

3.子应用配置

(1)跟主应用一样,也是不使用react的官方脚手架搭一个简单的react项目

搭建方式跟上面的主应用相同,不过需要改几个文件的内容。

按上面的步骤又搭建起一个工程后。

将public/index.html中的内容替换掉:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>子应用1</title>
</head>

<body>
    <div id="root"></div>
</body>

</html>

将src/App.js中的内容替换掉:

import React from 'react';

function App() {
    return <h1>Hello, 子应用1!</h1>;
}

export default App;

将src/webpack.config.js中的内容替换掉:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: __dirname + '/dist',
        filename: 'bundle.js',
    },
    devServer: {
        static: {
            directory: './dist',
        },
        port: 3002,
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env', '@babel/preset-react'],
                    },
                },
            },
        ],
    },
    plugins: [
        new ModuleFederationPlugin({
            name: 'microApp1',
            filename: 'remoteEntry.js',
            exposes: {
                './app': './src/App'
            },
            // shared: {
            //     react: { singleton: true },
            //     'react-dom': { singleton: true }
            // },
        }),
        new HtmlWebpackPlugin({
            template: './public/index.html',
        }),
    ],
};

将两个工程都运行起来,你点击应用1按钮就会发现,子应用已经集成到主应用里面了。