微前端
- 在大型项目中,往往会把项目中的某个区域或功能模块作为单独的项目开发,最终形成 微前端 架构,在微前端架构中,不同的工程可能出现下面的场景:
- 这涉及到很多非常棘手的问题:
- 如何避免公共模块重复打包;
- 如何将某个项目中一部分模块分享出去,同时还要避免重复打包;
- 如何管理依赖的不同版本;
- 如何更新模块;
- …
- webpack5 尝试着通过 模块联邦 来解决此类问题;
初始化工程
现有两个微前端工程,它们各自独立开发、测试、部署,但它们有一些相同的公共模块,并有一些自己的模块需要分享给其他工程使用,同时又要引入其他工程的模块;
home 项目
# 初始化 package.json
npm init -y
# 安装依赖
npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin
npm i jquery
// 修改 package.json
"scripts": {
"build": "webpack",
"dev": "webpack serve"
}
// 配置 webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
mode: 'development',
devtool: 'source-map',
devServer: {
port: 8080,
},
output: {
clean: true,
},
plugins: [ new HtmlWebpackPlugin() ]
};
// src/now.js
import $ from 'jquery';
export default function (container) {
const p = $('<p>').appendTo(container).text(new Date().toLocaleString());
setInterval(function () {
p.text(new Date().toLocaleString());
}, 1000);
}
// src/bootstrap.js
import $ from 'jquery';
import now from './now';
// 生成首页标题
$('<h1>').text('首页').appendTo(document.body);
// 首页中有一个显示当前时间的区域
now($('<div>').appendTo(document.body));
// src/index.js
import('./bootstrap');
active 项目
# 初始化 package.json
npm init -y
# 安装依赖
npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin
npm i jquery
// 修改 package.json
"scripts": {
"build": "webpack",
"dev": "webpack serve"
}
// 配置 webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
mode: 'development',
devtool: 'source-map',
devServer: {
port: 3000,
},
output: {
clean: true,
},
plugins: [ new HtmlWebpackPlugin() ]
};
// src/news.js
import $ from 'jquery';
export default function (container) {
const ul = $('<ul>').appendTo(container);
let html = '';
for (var i = 1; i <= 20; i++) {
html += `<li>新闻${i}</li>`;
}
ul.html(html);
}
// src/bootstrap.js
import $ from 'jquery';
import news from './news';
// 生成活动页标题
$('<h1>').text('活动页').appendTo(document.body);
// 活动页中有一个新闻列表
news($('<div>').appendTo(document.body));
// src/index.js
import('./bootstrap');
暴露和引用模块
active 项目需要使用 home 项目的 now 模块
-
home 项目暴露 now 模块
// webpack.config.js const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { plugins: [ new ModuleFederationPlugin({ // 模块联邦的名称 // 该名称将成为一个全部变量,通过该变量将可获取当前联邦的所有暴露模块 name: 'home', // 模块联邦生成的文件名,全部变量将置入到该文件中 filename: 'home-entry.js', // 模块联邦暴露的所有模块 exposes: { // key:相对于模块联邦的路径 // 这里的 ./now 将决定该模块的访问路径为 home/now // value: 模块的具体路径 './now': './src/now.js', }, }), ], };
-
active 项目引入 now 模块
// webpack.config.js const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { plugins: [ new ModuleFederationPlugin({ // 远程使用其他项目暴露的模块 remotes: { // key: 自定义远程暴露的联邦名 // 比如为 abc, 则之后引用该联邦的模块则使用 import "abc/模块名" // value: 模块联邦名@模块联邦访问地址 // 远程访问时,将从下面的地址加载 home: 'home@http://localhost:8080/home-entry.js', }, }), ], }; // src/bootstrap.js // 远程引入时间模块 import now from 'home/now' now($('<div>').appendTo(document.body));
home 项目需要使用 active 项目的 news 模块
-
active 项目暴露 news 模块
// webpack.config.js const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { plugins: [ new ModuleFederationPlugin({ // 模块联邦的名称 // 该名称将成为一个全部变量,通过该变量将可获取当前联邦的所有暴露模块 name: 'active', // 模块联邦生成的文件名,全部变量将置入到该文件中 filename: 'active-entry.js', // 模块联邦暴露的所有模块 exposes: { // key:相对于模块联邦的路径 // 这里的 ./news 将决定该模块的访问路径为 active/news // value: 模块的具体路径 './news': './src/news.js', }, }), ], };
-
home 项目引入 news 模块
// webpack.config.js const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { plugins: [ new ModuleFederationPlugin({ // 远程使用其他项目暴露的模块 remotes: { // key: 自定义远程暴露的联邦名 // 比如为 abc, 则之后引用该联邦的模块则使用 import "abc/模块名" // value: 模块联邦名@模块联邦访问地址 // 远程访问时,将从下面的地址加载 active: 'active@http://localhost:3000/active-entry.js', } }), ], }; // src/bootstrap.js // 远程引入新闻模块 import news from 'active/news' news($('<div>').appendTo(document.body));
处理共享模块
两个项目均使用了 jquery ,为了避免重复,可以同时为双方使用 shared 配置共享模块
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
// 配置共享模块
shared: {
// jquery为共享模块
jquery: {
singleton: true, // 全局唯一
},
},
}),
],
};
node👉 CommonJS 模块化细节
上一篇