浅析如何共享模块(npm发布共享、微前端、模块联邦)、如何使用Webpack5新特性模块联邦的具体流程及其适用场景

一、如何共享模块

1、NPM 方式共享模块

  想象一下正常的共享模块方式,就是 NPM。正常的代码共享需要将依赖作为 Lib 安装到项目,进行 Webpack 打包构建再上线。

  对于项目 A 与 B,需要共享一个模块时,最常见的办法就是将该模块抽成通用依赖并分别安装在各自项目中

2、微前端方式共享模块

  微前端:micro-frontends (MFE) 也是最近比较火的模块共享管理方式,微前端就是要解决多项目并存问题,多项目并存的最大问题就是模块共享,不能有冲突。

3、模块联邦方式

  作为 Webpack5 内置核心特性之一的 Federated Module:

  从图中可以看到,这个方案是直接将一个应用的包应用于另一个应用,同时具备整体应用一起打包的公共依赖抽取能力。

  让应用具备模块化输出能力,其实开辟了一种新的应用形态,即 “中心应用”,这个中心应用用于在线动态分发 Runtime 子模块,并不直接提供给用户使用

二、如何使用Webpack5 新特性模块联邦

  webpack5 引入联邦模式是为了更好的共享代码。 在此之前,我们共享代码一般用npm发包来解决。 npm发包需要经历构建,发布,引用三阶段,而联邦模块可以直接引用其他应用代码,实现热插拔效果。对比npm的方式更加简洁、快速、方便。

使用方法:

1、引入远程js

2、webpack配置

3、模块使用

1、引入远程 js

  假设我们有app1,app2两个应用,端口分别为3001,3002。 app1应用要想引用app2里面的js,直接用script标签即可。

// 例如app1应用里面index.html引入app2应用remoteEntry.js
<head>
    <script src="http://localhost:3002/remoteEntry.js"></script>
</head>

2、webpack配置

  app1的webpack配置:

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  //....
  plugins: [
    new ModuleFederationPlugin({
      name: "app1",
      library: { type: "var", name: "app1" },
      remotes: {
        app2: "app2",
      },
      shared: ["react", "react-dom"],
    }),
  ],
};

  对于app2的webpack配置如下

  plugins: [
    new ModuleFederationPlugin({
      name: "app2",
      library: { type: "var", name: "app2" },
      filename: "remoteEntry.js",
      exposes: {
        "./Button": "./src/Button",
      },
      shared: ["react", "react-dom"],
    })
  ],

  可以看到app1和app2的配置基本相同,除了app2 多了filename和exposes以外。参数解释:

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

library。UMD标准导出,和name保持一致即可。

remotes 声明需要引用的远程应用。如上图app1配置了需要的远程应用app2.

filename 远程应用时被其他应用引入的js文件名称。对应上面的remoteEntry.js

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

shared 依赖的包。

1、如果配置了这个属性。webpack在加载的时候会先判断本地应用是否存在对应的包,如果不存在,则加载远程应用的依赖包。

2、以app2来说,因为它是一个远程应用,配置了["react", "react-dom"] ,而它被app1所消费,所以webpack会先查找app1是否存在这两个包,如果不存在就使用app2自带包。 app1里面同样申明了这两个参数,因为app1是本地应用,所以会直接用app1的依赖。

3、模块使用

  对于app1/App.js代码使用app2的组件,代码如下:

import React from "react";
const RemoteButton = React.lazy(() => import("app2/Button"));
const App = () => (
  <div>
    <h1>Basic Host-Remote</h1>
    <h2>App 1</h2>
    <React.Suspense fallback="Loading Button">
      <RemoteButton />
    </React.Suspense>
  </div>
);
export default App;
// 具体这一行
// const RemoteButton = React.lazy(() => import("app2/Button")); 

  使用方式为:import('远程应用名/暴露的模块名'),对应webpack配置里面的name和expose。使用方式和引入一个普通异步组件无差别。

4、应用实例:vue3中应用

(1)案例1 home项目

new ModuleFederationPlugin({
  name: "home",
  filename: "remoteEntry.js",
  remotes: {
    home: "home@http://localhost:3002/remoteEntry.js",
  },
  exposes: {
    "./Content": "./src/components/Content",
    "./Button": "./src/components/Button",
  },
}),

(2)案例2 layout项目

 new ModuleFederationPlugin({
      name: "layout",
      filename: "remoteEntry.js",
      remotes: {
        home: "home@http://localhost:3002/remoteEntry.js",
      },
      exposes: {},
    }),

(3)layout中可以用home项目中的组件

import { createApp, defineAsyncComponent } from "vue";
import Layout from "./Layout.vue";

const Content = defineAsyncComponent(() => import("home/Content"));
const Button = defineAsyncComponent(() => import("home/Button"));

const app = createApp(Layout);

app.component("content-element", Content);
app.component("button-element", Button);

三、适用范围

1、由于share这个属性的存在,所以本地应用和远程应用的技术栈和版本必须兼容,统一用同一套。比如js用react,css用sass等。

2、联邦模块和微前端的关系:

  因为expose这个属性即可以暴露单个组件,也可以把整个应用暴露出去

  同时由于share属性存在,技术栈必须一致。

  所以加上路由,可以用来实现single-spa这种模式的微前端。

3、使用场景:

  新建专门的组件应用服务(中心应用)来管理所有组件和应用,其他业务层只需要根据自己业务所需载入对应的组件和功能模块即可。

  模块管理统一管理,代码质量高,搭建速度快。特别适用矩阵app,或者可视化页面搭建等场景。

  就是上面所说的:中心应用用于在线动态分发 Runtime 子模块,并不直接提供给用户使用。

  关于如何使用模块联邦主要参考文章:《Webpack5 新特性模块联邦介绍和应用 - https://www.haorooms.com/post/webpack5_new_featrue》

原文地址:https://www.cnblogs.com/goloving/p/15406647.html