目录
[remote 项目](#remote 项目)
[host 项目](#host 项目)
为什么讲这个呢,很多人觉得他不是微前端,也有人定义它也是微前端,看怎么理解了,我觉得他是一个去中心化技术,它可以让多个独立构建的应用之间,动态的调用彼此的模块。这种运行机制,可以让我们轻松的拆分应用,真正做到跨应用的模块共享。
他是跟
webpack5
强耦合的,是基于webpack5内置插件的 无须安装
介绍
模块联邦(Module Federation)是一种在前端应用中实现模块共享和跨应用程序共享的技术。它是由 webpack/lib/container/ModuleFederationPlugin 提供支持的。
模块联邦允许将应用程序拆分成多个独立的模块,这些模块可以独立开发、部署和维护。每个模块可以由不同的团队开发,并且可以在不同的应用程序中共享使用。这种方式可以提高开发效率,减少重复代码,并且使得应用程序更加灵活和可扩展。
webpack/lib/container/ModuleFederationPlugin 是 webpack 的一个插件,它提供了实现模块联邦的功能。通过使用该插件,我们可以将应用程序的不同模块打包成独立的 bundle,并且可以在其他应用程序中动态加载和使用这些模块。
使用模块联邦技术,我们可以实现以下应用场景:
-
微前端架构:将一个大型应用程序拆分成多个小型的子应用程序,每个子应用程序可以独立开发和部署,然后通过模块联邦技术将它们组合在一起。这样可以提高团队的独立性和开发效率。
-
插件化架构:将应用程序的不同功能模块打包成独立的插件,然后可以在其他应用程序中动态加载和使用这些插件。这样可以实现应用程序的可扩展性和灵活性。
-
跨应用程序共享:不同的应用程序可以通过模块联邦技术共享使用彼此的模块。这样可以避免重复开发相同的功能,提高代码的复用性和维护性。
总结来说,模块联邦技术通过 webpack/lib/container/ModuleFederationPlugin 插件提供了一种在前端应用程序中实现模块共享和跨应用程序共享的方式。它可以提高开发效率,减少重复代码,并且使得应用程序更加灵活和可扩展。
基本使用
要使用 webpack/lib/container/ModuleFederationPlugin 插件实现模块联邦,你需要按照以下步骤进行设置:
-
在你的主应用程序(host application)的 webpack 配置文件中,引入
webpack/lib/container/ModuleFederationPlugin
插件。 -
在插件的配置中,指定需要共享的模块名称和对应的远程入口文件。远程入口文件是包含共享模块的子应用程序(remote application)的打包文件。
-
在子应用程序的 webpack 配置文件中,同样引入
webpack/lib/container/ModuleFederationPlugin
插件。 -
在子应用程序的插件配置中,指定需要共享的模块名称和对应的本地入口文件。本地入口文件是子应用程序的打包文件。
-
在主应用程序的代码中,使用
import()
动态加载远程模块。这样可以在运行时从子应用程序中加载共享模块。
内部原理: 当你使用 webpack/lib/container/ModuleFederationPlugin 插件时,它会在构建过程中生成一个 manifest 文件。这个 manifest 文件包含了模块的映射关系和加载逻辑。
在主应用程序中,当你使用 import()
动态加载远程模块时,webpack 会根据 manifest 文件中的映射关系,找到对应的子应用程序,并从子应用程序中加载所需的模块。
注意事项:
-
确保主应用程序和子应用程序的 webpack 配置文件中都正确配置了
webpack/lib/container/ModuleFederationPlugin
插件。 -
确保主应用程序和子应用程序的模块名称和入口文件配置一致,这样才能正确地共享和加载模块。
-
注意版本兼容性,确保 webpack 和相关插件的版本与
webpack/lib/container/ModuleFederationPlugin
插件兼容。 -
在使用模块联邦时,要注意处理好模块的版本管理和依赖关系,以避免冲突和错误。
演示用法
构建两个项目一个host一个remote
场景就是host项目想使用remote项目的list模块,
初始化配置文件
下载依赖
pnpm init -y
pnpm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D
remote 项目
index.js
import("./bootstrap");
bootstrap.js
import { addList } from "./list";
let app = document.getElementById("app");
app.innerHTML = "<h2>remote</h2>";
addList();
异步加载
异步加载是一种优化技术,它可以在需要的时候动态地加载代码,而不是在页面加载时一次性加载所有的代码。这样可以提高页面的加载速度和性能。
例子中,通过在入口文件中引入一个代码文件来实现异步加载的方式是使用了动态导入(Dynamic Import)的语法。动态导入是ES6中的一个特性,它允许在运行时根据需要动态地加载模块。
在你的例子中,入口文件(main.js)通过使用import("./bootstrap")
语法来动态地加载一个名为bootstrap的代码文件。这样,当执行到这行代码时,浏览器会开始异步加载bootstrap.js文件,并在加载完成后执行其中的逻辑。
具体实现的原理是,当浏览器遇到动态导入语句时,会发起一个异步请求去加载指定的模块文件。一旦模块文件加载完成,浏览器会执行其中的代码。这样可以实现按需加载,提高页面的加载速度和性能。
需要注意的是,动态导入返回的是一个Promise对象,你可以使用then
方法来处理加载完成后的逻辑,或者使用async/await
语法来等待加载完成。
总结起来,通过在入口文件中使用动态导入语法,可以实现异步加载代码文件的逻辑,从而提高页面的加载速度和性能。
remote 项目的webpack 配置
const { Configuration } = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); //webpack5内置
/**
* @type {Configuration} //配置智能提示
*/
const config = {
mode: "none", //none 开发模式 production 生产模式
entry: "./index.js", //入口文件
output: {
// 输出文件
filename: "bundle.js", //输出文件名
},
devServer: {
// 本地服务器
port: 9002, //remote 9002
},
// 开发插件
plugins: [
//html插件
new HtmlWebpackPlugin({
template: "./index.html",
}),
// 远程模块插件
new ModuleFederationPlugin({
name: "remote", //name必填
filename: "remoteEntry.js", //filename必填 生成的文件名
exposes: {
"./addList": "./list.js", //暴露的模块
},
}),
],
};
module.exports = config;
host 项目
host 项目的webpack 配置文件
const { Configuration } = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
/**
* @type {Configuration} //配置智能提示
*/
const config = {
mode: "none", //none 开发模式 production 生产模式
entry: "./index.js", //入口文件
output: {
// 输出文件
filename: "bundle.js", //输出文件名
},
devServer: {
// 本地服务器
port: 9003, //remote 9002
},
// 开发插件
plugins: [
//html插件
new HtmlWebpackPlugin({
template: "./index.html",
}),
// 远程项目插件
new ModuleFederationPlugin({
name: "host", //name必填
filename: "hostEntry.js", //filename必填 生成的文件名
//对应关系remote对应的remote项目ModuleFederationPlugin的name 后面url对应的port以及对应ModuleFederationPlugin的filename
remotes: {
remote: "remote@http://localhost:9002/remoteEntry.js", //引入模块
},
}),
],
};
module.exports = config;
host项目使用模块 因为模块是异步加载的
webpack/lib/container/ModuleFederationPlugin 插件通常与异步加载(dynamic import)一起使用。
模块联邦的核心思想是将应用程序拆分成多个独立的模块,并在需要时动态加载这些模块。这种动态加载的方式可以带来以下好处:
减少初始加载时间:通过异步加载,可以将应用程序的初始加载时间减少到最小。只有在需要时才会加载特定的模块,而不是一次性加载所有模块。这可以提高应用程序的性能和用户体验。
按需加载:异步加载允许根据用户的操作和需求,按需加载特定的模块。这样可以避免加载不必要的模块,减少资源的浪费。
模块独立性:通过异步加载,每个模块可以独立开发、部署和维护。这样不同的团队可以并行开发不同的模块,而不会相互干扰。同时,模块之间的依赖关系也可以更加灵活地管理。
动态更新:异步加载使得模块联邦可以实现动态更新。当一个模块发生变化时,只需要重新加载该模块,而不需要重新加载整个应用程序。这可以提高开发效率和部署灵活性。
因此,为了实现模块联邦的上述好处,webpack/lib/container/ModuleFederationPlugin 插件通常与异步加载一起使用。通过异步加载,可以按需加载模块,提高性能、灵活性和开发效率。
//对应关系remote对应的remote项目的name addlist 对应的是key
import('remote/addList').then(({ addList }) => {
let app = document.querySelector('#app');
app.innerHTML = `
<h3>Host</h3>
`;
addList()
})
打完包观察一下
其实就是一个cdn引入为什么这么做呢?
在之前我们十个项目共用一个模块 会发到npm 例如1.0.0 这个模块要改动 1.0.1,那每一个项目都要去重新install 一下 很繁琐,而模块联邦是cdn 引入 无需 重新安装每次就是最新的