Webpack5 新特性模块联邦介绍和应用

1,513次阅读
没有评论

共计 3597 个字符,预计需要花费 9 分钟才能阅读完成。

特性

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

使用方法

1、引入远程 js

2、webpack 配置

3、模块使用

引入远程 JS

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

例如 app1 应用里面 index.html 引入 app2 应用 remoteEntry.js

 
    
  

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 的依赖。

模块使用

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

import React from "react";

const RemoteButton = React.lazy(() => import("app2/Button"));

const App = () => (

Basic Host-Remote

App 1

); export default App;

具体这一行

const RemoteButton = React.lazy(() => import("app2/Button")); 

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

适用范围

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

联邦模块和微前端的关系:因为 expose 这个属性即可以暴露单个组件,也可以把整个应用暴露出去。同时由于 share 属性存在,技术栈必须一致。所以加上路由,可以用来实现 single-spa 这种模式的微前端。

使用场景:新建专门的组件应用服务来管理所有组件和应用,其他业务层只需要根据自己业务所需载入对应的组件和功能模块即可。模块管理统一管理,代码质量高,搭建速度快。特别适用矩阵 app,或者可视化页面搭建等场景。

enter image description here

应用

1、next 项目应用 next 项目 1 的 next.config.js

  webpack: (config, options) => {const { buildId, dev, isServer, defaultLoaders, webpack} = options;
    const mfConf = {
      mergeRuntime: true, //experimental
      name: "next1",
      library: {type: config.output.libraryTarget, name: "next1"},
      filename: "static/runtime/remoteEntry.js",
      exposes: {"./exposedTitle": "./components/exposedTitle",},
      remotes: {
        next2: isServer
          ? path.resolve(
              __dirname,
              "../next2/.next/server/static/runtime/remoteEntry.js"
            )
          : "next2",
      },
    };
    if (!isServer) {config.output.publicPath = "http://localhost:3000/_next/";}
    withModuleFederation(config, options, mfConf);
    return config;
  }

next 项目 2 的 next.config.js

webpack: (config, options) => {const { buildId, dev, isServer, defaultLoaders, webpack} = options;
    const mfConf = {
      mergeRuntime: true, //experimental
      name: "next2",
      library: {type: config.output.libraryTarget, name: "next2"},
      filename: "static/runtime/remoteEntry.js",
      remotes: {
        next1: isServer
          ? path.resolve(
              __dirname,
              "../next1/.next/server/static/runtime/remoteEntry.js"
            )
          : "next1",
      },
      exposes: {"./nav": "./components/nav",},
      shared: ["lodash"],
    };

    withModuleFederation(config, options, mfConf);

    if (!isServer) {config.output.publicPath = "http://localhost:3001/_next/";}

    return config;
  }

ps 注意,还要配置

future: {webpack5: true},

vue3 中应用

案例 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 layout 项目

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

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);

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于1970-01-01发表,共计3597字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)