为了满足新老项目共同的业务需求,我们决定将使用Vue2和Webpack4的老项目升级为使用Webpack5和React17的新项目。此外,我们还希望通过使用Webpack5的模块联邦(module federation)特性,对老项目进行渐进式升级。
在此过程中,我们主要完成了以下功能:
- 项目升级到Webpack5
- React导出公共样式,实现组件样式隔离
- Vue引入并展示React组件
Vue-cli和Webpack5的升级
- 首先,我们需要升级Vue-cli。具体步骤如下:
// 全局安装最新版
yarn global add @vue/cli
// 在项目目录下执行
vue upgrade
- 接着,我们需要更新
webpack
和相应的loader和plugins,包括less
、less-loader
、postcss
、postcss-loader
、terser-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。进行升级或替换处理
- 更新依赖完成后,启动项目,根据提示修改devServer配置项。
disableHostCheck: true, => allowedHosts: 'all',
openPage: pageData.openPage, open:true, => open: [],
https: {} => server: {type: }
- 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
,增加域名或放到项目目录下。
提示:如果图片资源单独存在于域名根路径下,会引起此问题。
- 修改上述问题后,启动项目,打包,查看输入日志是否存在问题,进行修改。
React支持模块联邦
模块联邦主要用于组件打包,类似于Antd组件。如果组件存在语言等配置项,需要在每个导出的组件中增加配置。
- 创建
configLayout
组件,根据业务进行添加或扩展。非必须。
// ... 省略代码 ...
export function ConfigLayout({ children, lang = 'zh-CN' }) {
return (
<ConfigProvider locale={antI18n[lang]}>
<p>当前语言:{lang}</p>
{children}
</ConfigProvider>
)
}
export default ConfigLayout
- 导出组件List。
// ... 省略代码 ...
const Index: React.FC<Props> = (props) => {
return (
<ConfigLayout {...props}>
<List />
</ConfigLayout>
)
}
export default Index
- 修改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支持模块联邦
安装相关依赖
npm i vuereact-combined
,react/react-dom 版本与React项目版本一致npm i react@18.2.0 react-dom@18.2.0
。新建
bootstrap.js
,复制main.js
中的内容。
// ... 省略代码 ...
new Vue({
render: h => h(App),
}).$mount('#app')
main.js
修改内容为import('./bootstrap')
。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',
}
},
}),
- 新增组件展示。
<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组件,实现了技术架构的升级和项目的渐进式升级。