webpack5新增的ModuleFederationPlugin

  • 一种解决微前端的解决方案,
  • 共用组件升级时,避免所有依赖项目都要进行升级的痛点

webpack5的模块联邦 - 图1
创建2个项目目录,remote和host

remote远程组件

该项目代表的是被远程引用的共用组件库

安装项目依赖

  1. // webpack相关
  2. yarn add webpack webpack-cli webpack-dev-server html-webpack-plugin -D
  3. // 框架类相关
  4. yarn add react react-dom babel-loader @babel/core @babel/preset-react -D

设置webpack.config.js

  1. const path = require("path");
  2. const webpack = require("webpack");
  3. const htmlWebpackPlugin = require("html-webpack-plugin");
  4. const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
  5. module.exports = {
  6. mode: "development",
  7. entry: "./src/index.js",
  8. output: {
  9. publicPath: "http://localhost:3000/",
  10. },
  11. devtool: false,
  12. devServer: {
  13. port: 3000,
  14. },
  15. module: {
  16. rules: [
  17. {
  18. test: /\.jsx?$/,
  19. use: {
  20. loader: "babel-loader",
  21. options: {
  22. presets: ["@babel/preset-react"],
  23. },
  24. },
  25. exclude: /node_modules/,
  26. },
  27. ],
  28. },
  29. plugins: [
  30. new htmlWebpackPlugin({
  31. template: "./public/index.html",
  32. }),
  33. // 定义被引用时的名称,引入路径 ${name}/${exposes} -> remote/NewList
  34. new ModuleFederationPlugin({
  35. name: "remote",
  36. filename: "remoteEntry.js",
  37. // 提供组件时:exposes
  38. exposes: {
  39. "./NewList": "./src/NewList",
  40. },
  41. // 性能优化
  42. shared: {
  43. react: { singleton: true },
  44. "react-dom": { singleton: true },
  45. },
  46. }),
  47. ],
  48. };

组件中目录代码

  1. ├── package.json
  2. ├── public
  3. └── index.html
  4. ├── src
  5. ├── App.js
  6. ├── NewList.js
  7. ├── bootstrap.js
  8. └── index.js
  9. ├── webpack.config.js

package.json

  1. "scripts": {
  2. "start": "webpack serve",
  3. "build": "webpack"
  4. },

index.js

  1. import "./bootstrap";

App.js

  1. import React from "react";
  2. import NewList from "./NewList";
  3. export default (props) => {
  4. return (
  5. <div>
  6. <h1>App</h1>
  7. <hr />
  8. <h3>list</h3>
  9. <NewList
  10. list={[
  11. { id: 1, name: "js" },
  12. { id: 2, name: "webpack" },
  13. ]}
  14. />
  15. </div>
  16. );
  17. };

bootstrap.js

  1. import React from "react";
  2. import { createRoot } from 'react-dom/client';
  3. import App from "./App";
  4. const root = createRoot(document.getElementById("app"));
  5. root.render(<App />);

NewList.js

  1. import React from "react";
  2. export default (props) => {
  3. return (
  4. <ol>{props.list && props.list.map((l) => <li key={l.id}>{l.name}</li>)}</ol>
  5. );
  6. };

生成了remoteEntry.js

启动项目是会发现生成了remoteEntry.js文件,该文件就是供其它项目引入插件时使用。
该文件会提供组件的入口链接。

host项目

本地项目,引入远程组件。

安装项目依赖

  1. // webpack相关
  2. yarn add webpack webpack-cli webpack-dev-server html-webpack-plugin -D
  3. // 框架类相关
  4. yarn add react react-dom babel-loader @babel/core @babel/preset-react -D

设置webpack.config.js

  1. const path = require("path");
  2. const webpack = require("webpack");
  3. const htmlWebpackPlugin = require("html-webpack-plugin");
  4. const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
  5. module.exports = {
  6. mode: "development",
  7. entry: "./src/index.js",
  8. output: {
  9. publicPath: "http://localhost:8000/",
  10. },
  11. devServer: {
  12. port: 8000,
  13. },
  14. module: {
  15. rules: [
  16. {
  17. test: /\.jsx?$/,
  18. use: {
  19. loader: "babel-loader",
  20. options: {
  21. presets: ["@babel/preset-react"],
  22. },
  23. },
  24. exclude: /node_modules/,
  25. },
  26. ],
  27. },
  28. plugins: [
  29. new htmlWebpackPlugin({
  30. template: "./public/index.html",
  31. }),
  32. new ModuleFederationPlugin({
  33. name: "host",
  34. //使用的时:为remotes
  35. remotes: {
  36. remote: "remote@http://localhost:3000/remoteEntry.js",
  37. }
  38. }),
  39. ],
  40. };

本地项目中目录结构

  1. .
  2. ├── package.json
  3. ├── public
  4. └── index.html
  5. ├── src
  6. ├── App.js
  7. ├── bootstrap.js
  8. └── index.js
  9. └── webpack.config.js

index.js

  1. import "./bootstrap";

bootstrap.js

  1. import React from "react";
  2. import { createRoot } from 'react-dom/client';
  3. import App from "./App";
  4. const root = createRoot(document.getElementById("app"));
  5. root.render(<App />);

App.js

  1. import React from "react";
  2. // import 远程组件
  3. const RemoteNewList = React.lazy(() => import("remote/NewList"));
  4. export default (props) => {
  5. return (
  6. <div>
  7. <h1>Remote List</h1>
  8. <React.Suspense fallback="loading">
  9. <RemoteNewList list={[
  10. { id: 1, name: "remote js" },
  11. { id: 2, name: "remote webpack" },
  12. ]} />
  13. </React.Suspense>
  14. </div>
  15. );
  16. };