
本系列大纲
- 微前端模块联邦实践系列(1) - 微前端入门
- 微前端模块联邦实践系列(2) - 微前端与模块联邦
- 微前端模块联邦实践系列(3) - 第一个案例
- 微前端模块联邦实践系列(4) - subApp之间共享libraries
- 微前端模块联邦实践系列(5) - Path to Production
- 微前端模块联邦实践系列(6) - 集成React & Vue
- 微前端模块联邦实践系列(7) - 路由管理
- 微前端模块联邦实践系列(8) - 消息通信
声明
- 本系列使用的数据全部来自于jsonplaceholder暴露出来的公共API。根据API数据,主要分为以下几个模块:
- container:主要职责为提供基础的模块容器,显示当前user,切换user
- posts:显示当前用户下所有的posts,以及每一个post对应的comments
- albums:显示当前用户下所有的albums,以及在每一个album中的photos
- 文中出现的代码根据需要会进行部分截取,完整代码可以参看github repo,出现的部分命令可以自行参看各自的
package.json中运行的命令。
因此按照项目职责,分为三个app,container、posts以及albums。当container中切换用户之后,需要在posts以及albums对应的子app中显示对应用户的相应内容。
初始化posts (albums类似,不再赘述)
这里就做简化了,具体的配置可以直接参照github repo
webpack基本配置
// apps/posts/config/webpack.dev.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
output: {
filename: '[name].[contenthash].js',
},
devServer: {
port: 8081
,
hot: true,
historyApiFallback: true,
},
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
}),
],
}
暴露posts app, 配置模块联邦
// apps/posts/config/config.dev.js
const { ModuleFederationPlugin } = require('webpack').container
new ModuleFederationPlugin({
// The name of the remote app, which will be used to reference it in other apps
name: 'posts',
// The name of the file that will be created in the dist folder,
// which contains the metadata and exposed modules for the remote app
filename: 'remoteEntry.js',
// The modules that will be exposed by this remote app
exposes: {
// The key is the name under which the module will be exposed
// The value is the path to the module file within the remote app
'./PostsIndex': './src/index',
},
}),
初始化container
container app中的初始webpack配置和posts app中的配置并没有太多区别,只是container app中的开发端口配置为8080,而posts app的开发端口为8081
使用posts app,配置模块联邦
// apps/container/config/config.dev.js
new ModuleFederationPlugin({
// The name of the container app
name: 'container',
// Configuration for remote apps
remotes: {
// The key is the reference name of the remote app in the container app
// The value is the name of the remote app and the URL of the remote entry file
postsApp: 'posts@http://localhost:8081/remoteEntry.js',
},
})
container app中src/index.js作为webpack的入口,code如下:
// apps/container/src/index.js
import 'postsApp/PostsIndex'
const component = () => {
const element = document.createElement('div')
element.innerHTML = ['Hello', 'container app'].join(' ')
return element
}
const root = document.getElementById('containerRoot')
root.appendChild(component())
在没有配置模块联邦使用postsApp之前,使用npm run serve是可以直接在localhost:8080打开container app的,但是引入posts模块之后
import 'postsApp/PostsIndex'
会出现一个错误,如下:
原因是postsApp是异步引入的,因此之前的导入方式就要变一变。首先新增一个bootstrap.js,将index.js文件中的内容全部复制过去,然后将index.js整体作为动态导入
// apps/container/src/index.js
import('./bootstrap')
然后再次运行,就可以看到加载posts以及albums之后的页面了
Local Run & Integration Run
在上述提交的代码中,可以看到container/public/index.html中必须需要声明另外两个和subApp对应的rootId,不然subApp在container中挂载就会找不到对应app的root DOM节点,这样做不好的点是不能够自动挂载,必须要和containerApp协商好对应的rootId,我们将其中的代码稍微更改下
// apps/posts/src/index.js (albums类似)
const component = () => {
const element = document.createElement('div')
element.innerHTML = 'Hello, Posts app'
return element
}
export const mount = (el) => {
el.appendChild(component())
}
if (process.env.NODE_ENV === 'development') {
const root = document.getElementById('postsRoot')
if (root) {
mount(root)
}
}
我们将subApp的挂载权交由给container,使用container传递过来的root节点,这样就不需要协商dom节点的id是什么,以及应该放在页面的哪个地方。
为了兼顾能够同时在本地开发时启动subApp,所以我们这里做了对当前rootDom的判断,使其也能在本地运行。