webpack 模块联邦
1、基础配置
前提概述
模块联邦可以将多个独立运行的项目产生依赖关系,以下创建两个项目,让A项目依赖B项目的一个组件。(前三步两个项目配置相同)
(1)、安装webpack环境
npm init -y
npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D
(2)、创建入口文件
在根目录下创建 /src/index.js
(3)、配置webpack
在根目录下创建webpack.config.js
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin()
]
}
(4)、准备B项目资源
在package.json中配置启动脚本:
"scripts": {
"dev": "webpack-dev-server --port 3001",
...
},
在src下创建一个文件导出资源 命名为createHeader.js
export default () => {
const header = document.createElement('header')
header.innerText = 'header'
return header
}
修改webpack.config.js文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
mode: 'development',
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin(),
new ModuleFederationPlugin({
name: 'header', // 项目名称
filename: 'header.js', // 请求该项目暴露的依赖入口
remotes: {}, // 该项目需要远程依赖的配置
exposes: {
// 该项目暴露的依赖, 将刚刚写的createHeader.js暴露出去,外部访问路径为./createHeader
'./createHeader': './src/createHeader.js'
},
shared: {} // 改项目分享的公共模块
})
]
}
启动B项目
npm run dev
(5)、配置A项目
在package.json中配置启动脚本:
"scripts": {
"dev": "webpack-dev-server --port 3000",
...
},
修改webpack.config.js文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { ModuleFederationPlugin } = require('webpack').container
module.exports = {
mode: 'development',
entry: './src/index.js'
plugins: [
new HtmlWebpackPlugin(),
new ModuleFederationPlugin({
name: 'main',
filename: 'main.js',
remotes: {
// 配置依赖的模块将其命名为本项目的header模块,header(B项目的名称)@http://localhost:3001(B项目的host)/header.js(B项目的依赖入口)
'header': 'header@http://localhost:3001/header.js'
},
exposes: {},
shared: {}
})
]
}
在index.js中使用
;(async () => {
// 引用远程依赖的模块,由于远程加载需要异步方式获取 header(本项目的命名的模块名称)/createHeader (B项目暴露的外部访问路径)
const {default: createHeader} = await import('header/createHeader')
document.body.appendChild(createHeader())
})()
启动A项目 访问http://localhost:3000 即可看到header被引入了
npm run dev
2、react配置
前提概述
以下创建两个react项目,让A项目依赖B项目的一个组件。(第一步两个项目配置相同)
(1)、使用react脚手架搭建两个react项目
以A项目为例
npx create-react-app A
cd A
// 拉取配置文件
npm run eject
(2)、准备B项目资源
在scr/components下创建一个组件button.jsx
const Button = ({ children }) => {
return (
<button className="test-btn1">{children}</button>
)
}
export default Button
修改项目启动的端口 script/start.js 修改启动端口为3001
// const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3001;
修改config/webpack.config.js文件 加入模块联邦插件,并将定义好的button组件暴露出去
...
const { ModuleFederationPlugin } = require('webpack').container;
...
plugins: [
...
output: {
...
publicPath: process.env.NODE_ENV === 'development' ? 'http://localhost:3001/' : 'http://production.com/', // 必须要配置publicPath,否则请求的路径不对,找不到资源
...
}
...
new ModuleFederationPlugin({
name: 'component',
filename: 'component.js',
remotes: {},
exposes: {
'./Button': './src/components/button.jsx'
},
}),
....
]
启动项目
npm start
(3)、配置A项目
修改config/webpack.config.js文件 加入模块联邦插件,配置好依赖
...
const { ModuleFederationPlugin } = require('webpack').container;
...
plugins: [
...
new ModuleFederationPlugin({
name: 'main',
filename: 'main.js',
remotes: {
'component': 'component@http://localhost:3001/component.js'
},
exposes: {},
shared: {}
}),
....
]
在需要使用的地方直接调用,由于是远程加载所以需要用异步方式
import React, { Suspense } from 'react'
const Button = React.lazy(() => import('component/Button'))
function App() {
return (
<div className="App">
app
<Suspense fallback="loading">
<Button>app button</Button>
</Suspense>
</div>
);
}
export default App;
启动项目
npm run start
3、总结
模块联邦可以实现微前端,独立打包部署每一个应用,其原理是将公共文件单独打包成一个js,在引用的地方直接用js标签请求即可,但无法支持seo。