跳到主要内容

基于Webpack的模块联邦特性实现微前端

· 阅读需 5 分钟
iiLsss

为了满足新老项目共同的业务需求,我们决定将使用Vue2和Webpack4的老项目升级为使用Webpack5和React17的新项目。此外,我们还希望通过使用Webpack5的模块联邦(module federation)特性,对老项目进行渐进式升级。

在此过程中,我们主要完成了以下功能:

  • 项目升级到Webpack5
  • React导出公共样式,实现组件样式隔离
  • Vue引入并展示React组件

Vue-cli和Webpack5的升级

  1. 首先,我们需要升级Vue-cli。具体步骤如下:
  // 全局安装最新版
yarn global add @vue/cli
// 在项目目录下执行
vue upgrade
  1. 接着,我们需要更新webpack和相应的loader和plugins,包括lessless-loaderpostcsspostcss-loaderterser-webpack-plugin等。
  "webpack" => 与远程库版本的最好一致
"less": "^3.9.0", => "^4.1.3",
"less-loader": "^4.1.0", => "^11.1.3",
"postcss-loader": "^3.0.0", => "^7.3.3",
"terser-webpack-plugin": "^2.1.0", => "^5.0.0"

提示:以上只是简单罗列。每个项目的依赖不同,注意引入其他webpack的loader或plugins是否支持webpack5。进行升级或替换处理

  1. 更新依赖完成后,启动项目,根据提示修改devServer配置项。
disableHostCheck: true, => allowedHosts: 'all',
openPage: pageData.openPage, open:true, => open: [],
https: {} => server: {type: }
  1. Vue-cli升级后,存在URL路径转换错误。例如,不支持如果 URL 是一个绝对路径 (例如 /images/foo.png),它将会被保留不变。

解决方案1: 修改webpack中css loader配置项。

css: {
loaderOptions: {
css: {
url: {
filter: (url) => {
if (url[0] === '/') {
return false;
}
return true;
},
}
},
// ...
}

解决方案2:将采用绝对路径的图片/images/foo.png,增加域名或放到项目目录下。

提示:如果图片资源单独存在于域名根路径下,会引起此问题。

  1. 修改上述问题后,启动项目,打包,查看输入日志是否存在问题,进行修改。

React支持模块联邦

模块联邦主要用于组件打包,类似于Antd组件。如果组件存在语言等配置项,需要在每个导出的组件中增加配置。

  1. 创建configLayout组件,根据业务进行添加或扩展。非必须。
// ... 省略代码 ...

export function ConfigLayout({ children, lang = 'zh-CN' }) {

return (
<ConfigProvider locale={antI18n[lang]}>
<p>当前语言:{lang}</p>
{children}
</ConfigProvider>
)
}

export default ConfigLayout
  1. 导出组件List。
// ... 省略代码 ...

const Index: React.FC<Props> = (props) => {
return (
<ConfigLayout {...props}>
<List />
</ConfigLayout>
)
}

export default Index
  1. 修改webpack配置。
// ... 省略代码 ...

new ModuleFederationPlugin({
name: 'react',
filename: "remoteEntry.js",
exposes: {
'./Todo': './src/components/Todo/index.js',
},
shared: {
react: {
eager: true,
singleton: true,
requiredVersion: '18.2.0',
},
'react-dom': {
eager: true,
singleton: true,
requiredVersion: '18.2.0',
}
}
}),

Vue-cli支持模块联邦

  1. 安装相关依赖npm i vuereact-combined,react/react-dom 版本与React项目版本一致 npm i react@18.2.0 react-dom@18.2.0

  2. 新建bootstrap.js,复制main.js中的内容。

// ... 省略代码 ...

new Vue({
render: h => h(App),
}).$mount('#app')
  1. main.js修改内容为 import('./bootstrap')

  2. vue.config.js增加ModuleFederationPlugin。

// ... 省略代码 ...

new ModuleFederationPlugin({
name: 'saas',
filename: 'remoteEntry.js',
remotes: {
"remote": `remote@http://localhost:8080/remoteEntry.js`
},
shared: {
react: {
eager: true,
singleton: true,
requiredVersion: '18.2.0',
},
'react-dom': {
eager: true,
singleton: true,
requiredVersion: '18.2.0',
}
},
}),
  1. 新增组件展示。
<template>
<div id="app">
<div class="vue-content">
我是本身的vue组件
<HelloWorld />
</div>
<div class="react-content">
我是react组件
<List lang="en" />
</div>
</div>
</template>

<script>
import {applyReactInVue} from "vuereact-combined"

import List from 'remote/List'
import HelloWorld from './components/HelloWorld'
export default {
name: 'App',
components: {
HelloWorld,
List: applyReactInVue(List)
}
}
</script>

<style>
// ... 省略代码 ...
</style>

至此,我们已经成功地在Vue-cli项目中引入了React组件,实现了技术架构的升级和项目的渐进式升级。