咦?什么是模塊聯(lián)邦?哦!有點意思!

特性
webpack 5引入聯(lián)邦模式是為了更好的共享代碼。在此之前,我們共享代碼一般用npm發(fā)包來解決。npm發(fā)包需要經(jīng)歷構(gòu)建,發(fā)布,引用三階段,而聯(lián)邦模塊可以直接引用其他應(yīng)用代碼,實現(xiàn)熱插拔效果。對比npm的方式更加簡潔、快速、方便。
歡迎關(guān)注《前端陽光》,加入技術(shù)交流群,加入內(nèi)推群
使用方法
1、引入遠程js
2、webpack配置
3、模塊使用
引入遠程JS
假設(shè)我們有app1,app2兩個應(yīng)用,端口分別為3001,3002。app1應(yīng)用要想引用app2里面的js,直接用script標簽即可。
例如app1應(yīng)用里面index.html引入app2應(yīng)用remoteEntry.js
<head>
<script src="http://localhost:3002/remoteEntry.js"></script>
</head>
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以外。
參數(shù)解釋
name 應(yīng)用名,全局唯一,不可沖突。
library。UMD標準導(dǎo)出,和name保持一致即可。
remotes 聲明需要引用的遠程應(yīng)用。如上圖app1配置了需要的遠程應(yīng)用app2.
filename 遠程應(yīng)用時被其他應(yīng)用引入的js文件名稱。對應(yīng)上面的remoteEntry.js
exposes 遠程應(yīng)用暴露出的模塊名。
shared 依賴的包。
1、如果配置了這個屬性。webpack在加載的時候會先判斷本地應(yīng)用是否存在對應(yīng)的包,如果不存在,則加載遠程應(yīng)用的依賴包。
2、以app2來說,因為它是一個遠程應(yīng)用,配置了["react", "react-dom"] ,而它被app1所消費,所以webpack會先查找app1是否存在這兩個包,如果不存在就使用app2自帶包。app1里面同樣申明了這兩個參數(shù),因為app1是本地應(yīng)用,所以會直接用app1的依賴。
模塊使用
對于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('遠程應(yīng)用名/暴露的模塊名'),對應(yīng)webpack配置里面的name和expose。使用方式和引入一個普通異步組件無差別。
適用范圍
由于share這個屬性的存在,所以本地應(yīng)用和遠程應(yīng)用的技術(shù)棧和版本必須兼容,統(tǒng)一用同一套。比如js用react,css用sass等。
聯(lián)邦模塊和微前端的關(guān)系:因為expose這個屬性即可以暴露單個組件,也可以把整個應(yīng)用暴露出去。同時由于share屬性存在,技術(shù)棧必須一致。所以加上路由,可以用來實現(xiàn)single-spa這種模式的微前端。
使用場景:新建專門的組件應(yīng)用服務(wù)來管理所有組件和應(yīng)用,其他業(yè)務(wù)層只需要根據(jù)自己業(yè)務(wù)所需載入對應(yīng)的組件和功能模塊即可。模塊管理統(tǒng)一管理,代碼質(zhì)量高,搭建速度快。特別適用矩陣app,或者可視化頁面搭建等場景。

應(yīng)用
1、next項目應(yīng)用 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中應(yīng)用
案例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);
歡迎關(guān)注《前端陽光》,加入技術(shù)交流群,加入內(nèi)推群
原文:https://www.haorooms.com/post/webpack5_new_featrue
