使用webpack5模块联邦
WHAT(Module Federation 是什么?)
Module Federation [ˌfedəˈreɪʃn] 使 JavaScript 应用得以在客户端或服务器上动态运行另一个 bundle 的代码。
这其中的关键点是:动态,包含两个含义:
1、按需,可以把一个包拆开来加载其中一部分;
2、运行时,跑在浏览器而非 node 编译时;
另一个 bundle 的代码,之前应用之间做共享是在文件级或 npm 包级 export 成员,现在可以在应用级 export 成员属性。
Module Federation 里有两个主要概念 host 和 remote。每个项目可以是 host 也可以是 remote,也可以两个都是。
配置 remote
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
cache: {
type: "filesystem",
cacheDirectory: path.resolve(__dirname, "node_modules/.cache/webpack"),
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
publicPath: "http://localhost:8000/",
},
devServer: {
port: 8000,
},
optimization: {
usedExports: true,
},
devtool: false,
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-react"],
},
},
],
exclude: /node_modules/,
},
{
test: /\.png$/,
type: "asset/resource",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
new ModuleFederationPlugin({ // 这里是重点部分
filename: "remoteEntry.js",
name: "remote",
exposes: {
"./NewsList": "./src/NewsList",
},
}),
],
};
src/index.js
import("./bootstrap");
src/bootstrap.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
src/App.js
import React from "react";
import NewsList from './NewsList';
const App = () => (
<div>
<h2>本地组件NewsList</h2>
<NewsList />
</div>
);
export default App;
src/NewsList.js 这个组件导出给别人使用,在 webpack 配置中可以看到。
import React from "react";
export default ()=>(
<div>新闻列表</div>
)
配置 host
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
cache: {
type: "filesystem",
cacheDirectory: path.resolve(__dirname, "node_modules/.cache/webpack"),
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
publicPath: "http://localhost:9000/",
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
},
optimization: {
usedExports: true,
},
devtool: false,
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-react"],
},
},
],
exclude: /node_modules/,
},
{
test: /\.png$/,
type: "asset/resource",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
new ModuleFederationPlugin({ // 这里是重点部分
filename: "remoteEntry.js",
name: "host",
remotes: {
remote: "remote@http://localhost:8000/remoteEntry.js"
}
})
],
};
src/index.js
import("./bootstrap");
src/bootstrap.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
src/App.js 中导入了之前别人写好的 NewsList 组件,在 webpack 配置中可以看到。
import React from "react";
const RemoteNewsList = React.lazy(() => import("remote/NewsList"));
const App = () => (
<div>
<h2 >远程组件NewsList</h2>
<React.Suspense fallback="Loading NewsList">
<RemoteNewsList />
</React.Suspense>
</div>
);
export default App;
A 应用可以引用 B 整个应用,也可以应用 B 的页面和组件。这样一来,灵活性就非常大了。
配置参数
字段 | 类型 | 含义 |
name | string | 必传值,即输出的模块名,被远程引用时路径为${name}/${expose} |
library | object | 声明全局变量的方式,name为umd的name |
filename | string | 构建输出的文件名 |
remotes | object | 远程引用的应用名及其别名的映射,使用时以key值作为name |
exposes | object | 被远程引用时可暴露的资源路径及其别名 |
shared | object | 与其他应用之间可以共享的第三方依赖,使你的代码中不用重复加载同一份依赖 |
以上代码都可以在我的 demo 中看到:https://github.com/wuxianqiang/webacpk5-remote
评论