联邦模块(概述,实战应用,基本原理,未来展望)

发布于:2022-11-07 ⋅ 阅读:(15) ⋅ 点赞:(0) ⋅ 评论:(0)

联邦模块是什么

· 是Webpack 5 的新特性之一,允许在多个 webpack 编译产物之间共享模块、依赖、页面甚至应用

· 提供了一种轻量级的、在运行时,通过全局变量组合,在不同模块之前进行数据的获取

· 提供了一种解决应用集的官方方案。 每个构建都充当一个容器,也可将其他构建作为容器。通过这种方式,每个构建都能够通过从对应容器中加载模块来访问其他容器暴露出来的模块。

一句话:共享自己的模块,使用别人共享的模块
在这里插入图片描述
(联邦模块功能示意图)

应用场景和解决的问题

当我们一个公共模块被多个项目使用时,我们一般会把它打包成npm包,通过发包的方式进行进行组件/方法的复用。 当组件(方法)有一些更新时候,尤其是更改了某些存在的bug,就不得不通知依赖模块进行升版;如果存在多个依赖方,这种“发布npm-> 通知-> 更改多个项目->重新发布多个项目”模式无疑是低效率的;

联邦模块正是解决这种问题的一种方案,只需要更新一个项目,其他项目即可引用最新的模块。

在这里插入图片描述

实战应用

目录组成

packages                              # 各个demo项目主目录
 ├─demo1                                #  remote角色
  ├──src                                  #  目录 
   ├──bootstrap.js                          #  原入口文件代码
   ├──index.js                              #  新入口文件
  ├──webpack.config.js                    #  配置文件
 ├─demo2                                #  host角色
 ├─demo3                                #  Vite
 ├─demo4                                #  Umi

demo1和demo2的webpack.config.js

// demo1的webpack.config.js
 plugins: [
    new ModuleFederationPlugin({
      name: "demo1",
      filename: "demo1.js",
      remotes: {
        demo2: "demo2@http://localhost:3002/demo2.js",
        demo4: "demo4@http://localhost:8000/demo4.js",
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
        },
        "react-dom": {
          singleton: true,
        },
      },
    }),
    
// demo2的webpack.config.js    
 plugins: [
    new ModuleFederationPlugin({
      name: 'demo2',
      filename: 'demo2.js',
      exposes: {
        './demo2': './src/App.js',
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
        },
        'react-dom': {
          singleton: true,
        },
      },
    }),
  ],

name 应用名,全局唯一,不可冲突。

filename 远程应用时被其他应用引入的js文件名称。

remotes 声明需要引用的远程应用。

​ tip: remotes :{ 自定义 : 远端组件name@URL/远端组件filename }

exposes 远程应用暴露出的模块名。

shared 依赖的包。

  • 如果配置了这个属性。webpack在加载的时候会先判断本地应用是否存在对应的包,如果不存在,则加载远程应用的依赖包。
  • 以demo2来说,因为它是一个远程应用,配置了[“react”, “react-dom”] ,而它被demo1所消费,所以webpack会先查找app1是否存在这两个包,如果不存在就使用demo2自带包。 demo1里面同样申明了这两个参数,因为demo1是本地应用,所以会直接用demo1的依赖。

demo1的index.js和bootstrap.js

// index.js
import('./bootstrap');

// bootstrap.js
import Demo2 from "demo2/demo2";
import Demo4 from "demo4/demo4";

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <>
    <div>开始</div>
    <div>我是Demo1</div>
    <Demo2></Demo2>
    <Demo4></Demo4>
  </>
);

通过这样直接导入,就可以直接使用其他模块exposes出去的组件啦。

案例地址:

暂无上传到外网,下方评论给代码

基本原理

Webpack 会把 ModuleFederationPlugin 选项配置了 expose的组件单独打包出一份chunk,如图一所示; 而如果把这份chunk的URL和filename配置在remote属性下,即表示webpack会帮你去请求这份代码,然后帮你挂载到全局变量,并解析放入你的组件中。如图二所示。

我们也可以简单复现这remote这一步操作(在demo3有使用),通过script请求到这份chunk,然后如下面代码所示,即然使用到这个组件。

//index.html
  <script src="http://localhost:3002/demo2.js"></script>
  
//App.js
  const dynamicFederation = async (scope, module) => {
  const container = window[scope];
  return container.get(module).then(factory => {
    const Module = factory();
    return Module;
  });
};

const Demo2 = React.lazy(() => dynamicFederation('demo2', './demo2'));

在这里插入图片描述
在这里插入图片描述

问题

  • 联邦模块能用来代替microApp吗?

不能,两者不是同一生态位的应用, 联邦模块不具备样式隔离,监听路由等功能。但联邦模块可以与microApp结合使用。

  • webapack和vite用联邦模块能互相分享各自的组件吗?

不能。vite能引用webpack分享的组件,但webpack不能引用vite分享的组件,vite之间能互相引用。

原因:vite打包出来的chunk,浏览器请求完无法直接解析,而联邦模块说到底就是通过浏览器请求这份chunk,然后解析。

解决办法:​
1、使用webpack打包vite项目 2、使用插件,浏览器请求完这个chunk后,通过插件去解析。

  • 为什么入口文件index.js和真正的文件app.js之间多了一个bootstrap.js?

是为了避免远端的依赖(组件)还未请求,就开始加载渲染。如图所示。

在这里插入图片描述

未来展望

方向一:打造一个去中心化的应用集群

一个项目既可以为remote端,也可以为host端,可以使用数个不同其他项目的组件,也可以为数个其他项目提供不同的组件。

在这里插入图片描述

方向二:中心化的应用集群

把公共的组件放在一个服务里,专门用于共享,其他服务若需要用某个公共组件,就往这个公共服务里请求。

总结

联邦模块还是一个比较新的特性,有优点也有缺点。网上的案例也不多,容易在上真正项目踩坑时,需要自己去研究解决。

文章写得比较简陋,有许多深入的细节也没有写上,相信作为一篇入门的文章还是可以的。

好了,感谢大家的观看!若你对这篇文章有何不懂或者意见,欢迎下方评论,你的意见与指导是我分享的最大动力。