首页 > 其他分享 >一种解决多系统web应用的策略,Module Federation(模块联邦)

一种解决多系统web应用的策略,Module Federation(模块联邦)

时间:2023-04-18 17:01:06浏览次数:47  
标签:web const Federation App app2 Module react app1 main

前言

针对很多大型的web应用,往往会衍生出很多子应用,而这些子应用之间有时候又往往需要进行交互或者复用一些功能或者组件,这个时候有没有一个比较好的策略来实现这样的交互呢。答案是有的,试试webpack5提供的Module Federation

先来个示例

万事先实操,然后再谈别的,不付诸实践的想法都是空想。

准备

  • 准备是使用lerna作为管理多项目的工具,所以先全局安装npm install --global lerna

项目生成

  • 通过在文件目录下运行lerna init,生成以下目录

    mf-test

    packages // 项目文件夹

    .gitignore // git忽略文件

    lerna.json // lerna的配置文件

    package.json // 整个工程的配置文件

  • 新建三个空的文件夹,mainapp1app2,使用lerna create命令来生成,删掉生成的test文件夹以及lib文件夹,开始改造

    packages

    main
    app1
    app2

  • 分别在三个文件夹下增加一个最简单的src和webpack.config.js以及在已有的package.json中改造

    src/index.js(app1, app2, main)

    import ReactDOM from 'react-dom';
    import React from 'react';
    import App from './App';
    
    ReactDOM.render(<App />, document.getElementById('root'));
    

    src/App.js(app1, app2)

    import React, { useState } from 'react';
    // app1就是app1,app2就显示app2
    const App = () => {
      return (
        <div>
          <h2>App 1</h2>
        </div>
      )
    };
    
    export default App;
    

    src/App.js(main)

     import React, { useState } from 'react';
     const App1 = React.lazy(() => import('app1/App'));
     const App2 = React.lazy(() => import('app2/App'));
     // 引用远程的app1和App2去进行切换展示
     const App = () => {
        const [show, setShow] = useState(true);
        return (
          <div>
            <button onClick={() => setShow(!show)}>切换子页面</button>
            <React.Suspense fallback="Loading Button">
              { show ? <App1 /> : <App2 /> }
            </React.Suspense>
          </div>
        )
     };
    
     export default App;
    

    webpack.config.js,基本复用一份配置

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const { ModuleFederationPlugin } = require('webpack').container;
    const path = require('path');
    
    module.exports = {
      entry: './src/index',
      mode: 'development',
      devServer: {
        static: {
          directory: path.join(__dirname, 'dist'),
        },
        port: 3001, // main设置为3000,App1设置为3001,APP2设置为3002
      },
      output: {
        publicPath: 'auto',
      },
      module: {
        rules: [
          {
            test: /\.jsx?$/,
            loader: 'babel-loader',
            exclude: /node_modules/,
            options: {
              presets: ['@babel/preset-react'],
            },
          },
        ],
      },
      //http://localhost:3001/remoteEntry.js
      plugins: [
        new ModuleFederationPlugin({
          name: 'app1', // 模块的名称
          remotes: {
            // 定义远程模块的名称,app1就定义main和app2,app2就定义app1和main,main就定义app1和app2,这样他们就能互相引用对方模块中的组件
            main: `main@${getRemoteEntryUrl(3000)}`,
            app2: `app2@${getRemoteEntryUrl(3002)}`,
          },
          filename: 'remoteEntry.js', // 作为远程模块导出时的js文件名
          exposes: {
            './App': './src/App' // 需要导出以供其它模块使用的组件或方法
          },
          shared: { react: { singleton: true }, 'react-dom': { singleton: true } }, // 公共的第三方库,这样就不会出现子项目中一些第三方库版本不一致导致的一些兼容问题
        }),
        new HtmlWebpackPlugin({
          template: './public/index.html',
        }),
      ],
    };
    
    function getRemoteEntryUrl(port) {
      const { CODESANDBOX_SSE, HOSTNAME = '' } = process.env;
    
      // Check if the example is running on codesandbox
      // https://codesandbox.io/docs/environment
      if (!CODESANDBOX_SSE) {
        return `//localhost:${port}/remoteEntry.js`;
      }
    
      const parts = HOSTNAME.split('-');
      const codesandboxId = parts[parts.length - 1];
    
      return `//${codesandboxId}-${port}.sse.codesandbox.io/remoteEntry.js`;
    }
    

    package.json,就是加一些项目依赖

    {
      "name": "@mf-test/app1", // 这个名称随便定义,目前我是定义的@mf-test/app1,@mf-test/app2,@mf-test/main
      "version": "0.0.0",
      "private": true,
      "devDependencies": {
        "@babel/core": "7.18.6",
        "@babel/preset-react": "7.18.6",
        "babel-loader": "8.2.5",
        "html-webpack-plugin": "5.5.0",
        "serve": "14.0.0",
        "webpack": "5.72.1",
        "webpack-cli": "4.10.0",
        "webpack-dev-server": "4.8.1"
      },
      "scripts": {
        "start": "webpack-cli serve",
        "build": "webpack --mode production",
        "serve": "serve dist -p 3001",
        "clean": "rm -rf dist"
      },
      "dependencies": {
        "react": "^16.13.0",
        "react-dom": "^16.13.0"
      },
      "useWorkspaces": true,
      "description": "> TODO: description",
      "author": "",
      "homepage": "",
      "license": "ISC",
      "publishConfig": {
        "registry": "https://bnpm.byted.org/"
      }
    }
    

运行

  • 在根目录下执行lerna bootstrap去安装对应需要的包,然后npm run start就是可以跑起来了,效果如图:

  • 点击按钮就可以切换对应的子应用。

小结

至此,已经完成了一个最基本的多子系统应用,可以完成基本的子系统切换,对于一些大型的项目改造来说还是比较有益的,在此基础上可以增加一个专门存放公共组件方法的子系统,提供给所有的其它子系统使用。它的原理说白了,就是异步加载模块之后同步执行模块去处理互相之间的引用,至于再底层一点的实现,后面有时间可以看看它的源码再说。

标签:web,const,Federation,App,app2,Module,react,app1,main
From: https://www.cnblogs.com/aloneMing/p/17326898.html

相关文章

  • jsp Web实现文件上传下载功能实例解析
    ​ 4GB以上超大文件上传和断点续传服务器的实现随着视频网站和大数据应用的普及,特别是高清视频和4K视频应用的到来,超大文件上传已经成为了日常的基础应用需求。但是在很多情况下,平台运营方并没有大文件上传和断点续传的开发经验,往往在网上找一些简单的PHP或者Java程序来实现基......
  • 把闲置安卓手机变成 NAS 服务器:KSWEB
    之前给大家分享过把闲置的安卓手机变成下载机、电脑扩展屏幕、蓝牙音箱、监控摄像头、电脑硬件监控屏等。今天教大家如何变成 NAS 服务器,可以搭建个人博客、或者用来备份文件、搭建私人家庭影库中心等。这里需要用到「KSWEB」这款服务器应用,无需手机ROOT,只要是安卓手机(安卓8......
  • 什么是全栈工程师,为什么全栈开发用Python,Python web全栈开发到底有多高薪?
    我们经常听到全栈工程师这个词语。那么很多小伙伴还是不明所以,什么是全栈工程师?为什么全栈开发用Python?Pythonweb全栈开发到底有多高薪?一、什么是Pythonweb全栈工程师?全栈工程师是指掌握多种技能,并能利用多种技能独立完成产品的人。也叫全端工程师(同时具备前端和后台能力),英文F......
  • Java Web实现文件上传下载功能实例解析
    ​ 第一点:Java代码实现文件上传FormFilefile=manform.getFile();StringnewfileName= null;Stringnewpathname= null;StringfileAddre= "/numUp";try{    InputStreamstream=file.getInputStream();// 把文件读入    StringfilePath=request.......
  • Abp框架Web站点的安全性提升
    本文将从GB/T28448-2019《信息安全技术网络安全等级保护测评要求》规定的安全计算环境中解读、摘要若干安全要求,结合Abp框架,对站点进行安全升级。【身份鉴别】应对登录的用户进行身份标识和鉴别,身份标识具有唯一性,身份鉴别信息具有复杂度要求并定期更换。解决方案设置密码......
  • Apifox 更新 | WebSocket 接口调试功能上线!
    Apifox 新版本上线啦!欢迎升级使用:界面右上角【设置】-> 【关于Apifox】->【检查更新】看看本次版本更新主要涵盖的重点内容,有没有你所关注的功能特性:支持WebSocketAPI;自动化测试功能升级;Web端新增部分功能快捷键;支持自定义设置版本更新及安装提醒。01支持WebSocketAPI最新......
  • wangeditor5 vue2 安装后运行报错 Error in ./node_modules/@wangeditor/editor/d
    问题解决方法:原因就是引入的链接不对,使用cnpm、npm、pnpm、yarn安装插件的时候,路径都是不一样的,所以要对应着改路径就可以了链接:https://github.com/wangeditor-team/wangEditor/issues/4041......
  • vSphere Web Client 添加主机进VSAN集群时报错“SAN 主机移至目标群集: vSAN 群集的 U
    案例描述vSphereWebClient添加主机进VSAN集群时,报“无法将vSAN主机移至目标群集:vSAN群集的UUID不匹配(主机:5223a6c9-cf94-f978-1abb-9906506626be,目标:523ae663-623b-e2fc-39e3-43b15c5ca801)。”错误。原因分析是因为该esxi主机已经加入过其它集群,和现在新加......
  • TWebBrower 动态改变大小
    注意:Panel1.BevelOuter:=bvNone;unitUnit2;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,OleCtrls,SHDocVw,ExtCtrls;typeTForm2=class(TForm)Panel1:TPanel;......
  • webstorm Can't use Subversion command line client: svn
    https://www.visualsvn.com/downloads/下载: ApacheSubversioncommandlinetools解压,安装,webstrorm里面点击fix,设置svn.exe的绝对路径就可以了。......