Open vivipure opened 1 year ago
负责的项目一直使用 Webpack 5 进行构建,开发时 热更新速度 和 打包速度 总体上还行,但是和 Vite 还是有些差距,因此特意花费时间来进行构建工具的升级
Webpack 5
Vite
项目技术栈: Vue 2.7 + TS
使用官方提供的插件即可
import vue from '@vitejs/plugin-vue2' ... plugins: [vue()] ...
css: { preprocessorOptions: { less: { additionalData: '@import "./src/assets/css/var.less";', }, }, },
项目使用 cross-env 设置环境变量,再通过构建工具暴露到全局变量中
cross-env
define: { HTTP_ENV: JSON.stringify(process.env.http_env), },
项目的业务中使用 xhtml 作为模板文件,用于字符串的组装。在webpack 中使用 loader进行配置
xhtml
webpack
loader
{ test: /\.(xhtml)$/, type: 'asset/source', },
在 vite 中可以使用 ?raw ,但是每个导入的都需要加,很麻烦。所以我写了一个插件,可以直接进行转化
?raw
function xhtmlPlugin(): Plugin { return { name: 'xhtmlPlugin', transform(code, id, options?) { if (id.endsWith('.xhtml')) { let buf = fs.readFileSync(id).toString() // 过滤文本中存在的 `,${, 避免代码生成异常 if(buf.includes('`')) { buf = buf.replace(/`/g, '\\`') buf = buf.replace(/\${/g, '\\${') } return { code: `export default \`${buf}\`;`, }; } }, } }
迁移的难点主要在于 Module Federation , 项目使用了 Webpack 5 的 Module Federation 作为组件库,因此需要 Vite 也进行支持
Module Federation
这里我使用了 vite-plugin-federation
import federation from '@originjs/vite-plugin-federation' ... federation({ name: 'remote-app', filename: 'remoteEntry.js', shared: { ...pkg.dependencies, }, remotes: { microFE: { external: federationUrl, format: 'var', from: 'webpack', }, }, }) ...
配置之后,在开发模式时,无法使用测试环境的 remote ,会产生跨域的问题,因此写了一个插件进行兼容
remote
const adaptiveModuleFederationPlugin = function (federationConfig: Parameters<typeof federation>[0]) { const adpativePlugin = () => ({ name: 'adaptiveModuleFederation', config(config, { command }) { if (command === 'serve') { if (!config.server.proxy) { config.server.proxy = {} } const remotes = federationConfig.remotes! const remoteKeys = Object.keys(remotes) remoteKeys.forEach(key => { const urlConfig = remotes[key] let url = '' if (typeof urlConfig === 'string') { url = urlConfig } else if (typeof urlConfig === 'object') { url = urlConfig.external } const preffixUrl = url.substring(0, url.lastIndexOf('/')) const endUrl = url.substring(url.lastIndexOf('/')) const proxyPath = '/proxy' + key const reg = new RegExp(`^${proxyPath}`) config.server.proxy[proxyPath] = { target: preffixUrl, changeOrigin: true, rewrite: path => path.replace(reg, ''), } const updateUrl = proxyPath + endUrl if (typeof urlConfig === 'string') { federationConfig.remotes![key] = updateUrl } else if (typeof urlConfig === 'object') { federationConfig.remotes![key].external = updateUrl } }) } }, }) return [adpativePlugin(), federation(federationConfig)] }
插件原理很简单,将地址代理到本地,即可避免跨域
... plugins: [...adaptiveModuleFederationPlugin({...}),] ...
搞定完上面这些,就可以正常运行项目了。提升十分明显。Webpack 5 热更新时间大概 1s 多,Vite 直接无感,使用十分流畅。
开发模式没有了问题,就开始进行打包。打包表现的很正常,得到构建后的文件后,开始本地运行。
What? 页面空白
打开 devtools, 报错了,无法从 Vue 中找到 defineComponent. 随后便进行各种尝试,最终在去除掉 config 中的 shared 后,项目可以正常运行
Vue
defineComponent
config
shared
... ...adaptiveModuleFederationPlugin({ shared: [] }) ...
查看了使用的公共组件,还是存在问题,element-ui 的组件无法进行渲染。
element-ui
shared 配置当前服务的共享模块,作为 host 端不进行配置,无法保证公共组件的加载。
host
我在vite-plugin-federation的仓库中查找到了
vite-plugin-federation
https://github.com/originjs/vite-plugin-federation/issues/248#issuecomment-1260875464
组件库对于 vue 是配置了 singleton: true,这可能是导致无法正常加载的主要原因
vue
singleton: true
除了上面这个问题,我在使用 @vitejs/plugin-legacy ,也遇到和 vite-plugin-federation 不兼容的情况, 具体可看这个
@vitejs/plugin-legacy
https://github.com/vitejs/vite/issues/6133
最终,我放弃了。插件目前不支持 singleton属性,除非我把组件库改成 Vite 构建的,这样的改动成本太大了。
singleton
最终我还是清除了 Vite 的依赖,如果只是为了优化热更新的时间,而配置两个构建工具,实在不太合适。
在这个过程中,还是有收获的。比如一些 Webpack 功能的迁移,Vite 插件的编写。
Webpack
希望后续上面提到的问题能被解决,后面有时间我再继续进行折腾吧。
一. 写在前面
负责的项目一直使用
Webpack 5
进行构建,开发时 热更新速度 和 打包速度 总体上还行,但是和Vite
还是有些差距,因此特意花费时间来进行构建工具的升级项目技术栈: Vue 2.7 + TS
二. 适配过程
2.1 Vue 功能支持
使用官方提供的插件即可
2.2 less 全局变量
2.3 环境变量
项目使用
cross-env
设置环境变量,再通过构建工具暴露到全局变量中2.4 xhtml 处理
项目的业务中使用
xhtml
作为模板文件,用于字符串的组装。在webpack
中使用loader
进行配置在 vite 中可以使用
?raw
,但是每个导入的都需要加,很麻烦。所以我写了一个插件,可以直接进行转化2.5 Module Federation
迁移的难点主要在于
Module Federation
, 项目使用了Webpack 5
的Module Federation
作为组件库,因此需要Vite
也进行支持这里我使用了 vite-plugin-federation
配置之后,在开发模式时,无法使用测试环境的
remote
,会产生跨域的问题,因此写了一个插件进行兼容插件原理很简单,将地址代理到本地,即可避免跨域
三. 运行效果
搞定完上面这些,就可以正常运行项目了。提升十分明显。
Webpack 5
热更新时间大概 1s 多,Vite
直接无感,使用十分流畅。四. 滑铁卢
4.1 遇到问题
开发模式没有了问题,就开始进行打包。打包表现的很正常,得到构建后的文件后,开始本地运行。
What? 页面空白
打开 devtools, 报错了,无法从
Vue
中找到defineComponent
. 随后便进行各种尝试,最终在去除掉config
中的shared
后,项目可以正常运行查看了使用的公共组件,还是存在问题,
element-ui
的组件无法进行渲染。shared
配置当前服务的共享模块,作为host
端不进行配置,无法保证公共组件的加载。4.2 寻找答案
我在
vite-plugin-federation
的仓库中查找到了组件库对于
vue
是配置了singleton: true
,这可能是导致无法正常加载的主要原因4.3 其他问题
除了上面这个问题,我在使用
@vitejs/plugin-legacy
,也遇到和vite-plugin-federation
不兼容的情况, 具体可看这个4.4 如何处理
最终,我放弃了。插件目前不支持
singleton
属性,除非我把组件库改成 Vite 构建的,这样的改动成本太大了。五. 最后
最终我还是清除了
Vite
的依赖,如果只是为了优化热更新的时间,而配置两个构建工具,实在不太合适。在这个过程中,还是有收获的。比如一些
Webpack
功能的迁移,Vite
插件的编写。希望后续上面提到的问题能被解决,后面有时间我再继续进行折腾吧。