最终鼓起勇气把公司五年的webpack老项目改成了vite
一.【 升级原因 】
工程现状:
1. 项目较早 + 代码量大 + 插件类库版本低 + 各个节点优化空间逐渐变小
2. 为接下来Pro工程瘦身做准备;
造成的困扰:
PS:在以上问题的基础上,webpack已经变得非常不好用了
开发环境服务的不稳定性,严重影响开发效率和心情;
打包后输出文件过大,导致运行环境速度慢、系统性能低;
改造目标:
1. webpack 升级 为 vite;
2. node版本调整;
3. sass 版本升级;
二.【 改造思路 】
版本库描述:
1. 整体保持vue@2.5.10核心基础库不动
2. 使用vite@2.8.0替换webpack@4.16.0
3. NodeJs版本v14.19.1升级到v18.7.0(后因版本过高,降回原版本v14.19.1)
4. Npm版本v6.14.16升级到v8.15.0
5. 升级node-sass@7.0.3到node-sass@7.0.3(后因版本过高,降回原版本v4.7.2)
6. 使用sass@1.55.0
7. 升级sass-loader@13.1.0到sass-loader@7.0.3
8. 引用vite-plugin-vue2@1.9.3使vue2支持vite
思路描述:
按需调整和配置,修改编译环境和运行环境的错误,最终经过测试阶段,代码推送线上
分支保全方案:
origin/dev dev环境源代码
origin/propre2 pre环境源代码
origin/pro 线上环境源代码
origin/config_vite_nyz_221108 vite配置测试分支
origin/config_webpack_backup_nyz_221109 webpack配置备份分支(拉取自pro分支)
三.【 升级报告 】
类目 | webpack(升级前) | vite(升级后) |
---|---|---|
本地服务启动时长 | 230 秒 | 3 秒 |
修改后热更新时长 | 23 秒 | 1 秒 |
本地服务性能 | 更新慢 & 服务经常崩溃 | 更新快 & 服务稳定 |
执行打包任务时长 | 300秒 | 55 秒 |
打包后输出文件大小 | 37.0 MB | 19.8 MB |
打包后输出文件个数 | 1372 个 | 787 个 |
依赖包优化后个数 | 103 个 | 82 个 |
运行环境兼容性验证 | -- | -- |
四.【 执行过程 】
- 初始化一个vite项目
// 注意:这里vite的版本采用2.8.0的,最新的版本创建后续会出现问题
// 要选择vanilla原生项目 , 因为选择vue会默认走vue3,造成与当前版本不匹配
npm init vite@2.8.0
- 将原项目的src文件夹和其他用到的文件/文件夹移入新项目
- 合并package.json的依赖项,script命令抛弃webpack,保留vite的两条指令
"scripts": {
"serve": "cross-env NODE_ENV=development vite",
"build": "cross-env NODE_ENV=production vite build"
},
- 删除外层初始化的main.js,移入或保留原项目的main.js到src下
-
index.html修改入口引入方式
<script type="module" src="src/main.js"></script>
-
main.js修改根的挂载方式
// // 原代码 // new Vue({ // el: '#app', // router, // store, // render: h => h(App) // }) // 目标代码 new Vue({ router, store, render: h => h(App) }).$mount('#app')
-
使用cnpm快速下载相关依赖包
cnpm install
-
NodeJs版本不一致,导致[node-sass]找不到并报错,直接拉取latest版本
cnpm install --save-dev sass sass-loader node-sass
-
启动本地服务报错:require is not defined
原因:vite默认使用es6标准的 import 的导入方式,不支持require引入
// 下载 cnpm install vite-plugin-require-transform
// vite.config.js 引入 import requireTransform from 'vite-plugin-require-transform' // vite.config.js 使用 defineConfig({ plugins: [ createVuePlugin(), requireTransform({ fileRegex: /.vue$|.js$|.json$|.ts$|.tsx$/ }) ] })
-
服务编译的时候,头部注释信息报错 , 如下图,
原因:语法编译不通过
解决:涉及文件较多,全部找出来注掉就可以了
-
编译报错:Failed to fetch dynamically imported module
原因:vue文件引入错误,构建过程无法补全文件后缀并读取
解决:把vue和js相关引入代码全部加上后缀 ( 涉及代码较多,找出来全部改掉 )
-
样式的deep语法报错,找出来全部替换掉
/deep/ 替换为 ::v-deep
-
编译报错:process is not defined
// vite.config.js define: { 'process.env': {} }
-
项目中使用的环境变量报错
解决:webpack升级vite后,需要创建环境变量文件并使用vite的环境变量
process.env // webpack import.meta.env // vite
-
编译报错:Module “path” has been externalized for brower compatibility and cannot be accesed in client code
原因:path模块无法在客户端代码中使用 , webpack自动帮我们处理了node内置模块 , vite没有
解决:安装 path-browserify 代替 path 模块
cnpm install path-browserify
import path from 'path' 改为 import path from 'path-browserify'
-
require兼容配置,某些三方包中的require语法,在开发环境正常,打包环境报错
// vite.config.js build:{ commonjsOptions: { transformMixedEsModules: true } }
-
打包报错:[commonjs] Identifier '_vite_plugin_require_transform_Component' has already
原因:错误所指的意思是echarts 库中声明的变量已存在,还有再声明的必要吗?
解决:实则该变量并没有重复声明,所以改动CDN引入的方案,并删掉页面import引入语句
注意:CDN要引入对应的版本
jsdelivr相关库文件的CDN地址:www.jsdelivr.com/package/npm…
// echart主入口和单独引入的功能 改为cdn引入 // index.html 引入以下代码 <script src="https://cdn.jsdelivr.net/npm/echarts@3.8.5/dist/echarts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/echarts@3.8.5/map/js/china.js"></script>
-
打包报错:'default' is not exported by node_modules/_core-js@2.6.12@core-js
解决:rollup本身是不支持CommonJS的,使用了这个插件,就可以解析CommonJS模块了
rollup.js相关配置资料:rollupjs.org/guide/en/#o…
cnpm install @rollup/plugin-commonjs -D
// vite.config.js import commonjs from '@rollup/plugin-commonjs'; plugins: [ commonjs() ]
-
打包报错:Error: 'default' is not exported by node_modules/_throttle-debounce@1.1.0
原因:该代码中的防抖节流方法没有默认抛出
解决:使用自定义封装方法或工具库平替即可
-
打包成功后部署至测试环境:Uncaught TypeError: Illegal invocation,报错如下图
原因:查看报错位置,可能是因为代码编译转换过程产生的错误
解决:故调整库版本和引入方式
1. es6-promise 引入方式调整 // require引入方式去掉 require('es6-promise').polyfill() 改为 // index.html 引入 <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script
2. @rollup/plugin-commonjs 该库版本降级调整 @23.0.2 降至 @13.0.2
-
测试环境引入文件404,如图
原因:实际上这个文件是存在的,但它的文件名携带%,导致get的时候被浏览器转译丢失
查找:因为 style标签中的 rel="stylesheet/scss" 导致 [/] 转为[%2F] 从而报错
解决:干掉报错部分的这段原代码,重新编译
-
路径别名配置平替webpack开发环境和打包环境用到的@
// vite.config.js resolve: { // 路径相关配置项 alias: [ // 文件别名配置 { find: '@', replacement: path.resolve(__dirname, 'src') } ] },
-
忽略扩展名配置,本项目文件后缀已手动补全,此项配置用作兜底方案
官方提示:注意,不建议忽略自定义导入类型的扩展名 (例如: .vue) ,因为它会影响 IDE 和类型支持
// vite.config.js resolve: { extensions: [ // 导入时想要省略的扩展名列表。 '.vue', '.js' ], },
-
为保证项目在浏览器的兼容性,打包目标选择最低
// vite.config.js build: { // 打包相关配置 target: 'es2015' }
-
package.json文件中无用的库全部去掉,可以优化项目体积和编译速度
-
可选优化升级:配置Gzip压缩
cnpm install vite-plugin-compression -D
// vite.config.js import viteCompression from 'vite-plugin-compression' plugins: [ viteCompression({ verbose: true, disable: false, threshold: 10240, // 阈值 algorithm: 'gzip', ext: '.gz', }) ]
-
可选优化升级:提升vite项目的浏览器兼容性
cnpm install @vitejs/plugin-legacy -D
// vite.config.js import legacyPlugin from '@vitejs/plugin-legacy' plugins: [ legacyPlugin({ targets: ['chrome 52'], // 需要兼容的目标列表,可以设置多个 additionalLegacyPolyfills: ['regenerator-runtime/runtime'] // 面向IE11时需要此插件 renderLegacyChunks: true, polyfills: [ 'es.symbol', 'es.array.filter', 'es.promise', 'es.promise.finally', 'es/map', 'es/set', 'es.array.for-each', 'es.object.define-properties', 'es.object.define-property', 'es.object.get-own-property-descriptor', 'es.object.get-own-property-descriptors', 'es.object.keys', 'es.object.to-string', 'web.dom-collections.for-each', 'esnext.global-this', 'esnext.string.match-all' ] }) ]
-
最终目录结构
PS:
-
删掉原项目webpack相关配置:* /build** & */config
-
新增:/public
-
原项目的 /static 文件夹下内容移至 /public 下
-
根目录新建:vite.config.js & .env.development & .env.production
-
-
测试发现:城市地图模块的动态导入数据运行和打包报错
原因:vite运行环境和编译环境都会抛出错误,因为此写法不被支持
// 报错的代码: import(``) function showProvince(eName, param) { import(`@/utils/map/province/${eName}.json`).then(res => { const data = JSON.parse(JSON.stringify(res)) that.$nextTick(() => { echarts.registerMap(param, data) initEcharts(param) }) }) }
解决:采用请求的方式动态获取城市地图的轻量数据,并在nginx配置支持json文件的get获取
function showProvince(eName, param) { axios.get(`/map/province/${eName}.json`).then(res => { if (res.status === 200 && res.data) { const data = JSON.parse(JSON.stringify(res.data)) that.$nextTick(() => { echarts.registerMap(param, data) initEcharts(param) }) } }) }
-
测试项目依赖包的install和build发现:node-sass下载会报错,打包会任务循环无法
解决方案:因为node和node-sass版本不匹配
node降至v14.19.1
node-sass降至@4.7.2
-
对于vite打包的项目进行兼容性测试(重要)
(1)通过CanIUse查看API各个浏览器兼容性
图片参考:
(2)核心关注使用率较高的几款PC端浏览器
图片参考:
(3)根据官方给出的Api兼容表,验证浏览器的兼容性
参考内容:可以使用比较成熟的几款云端虚拟机自动化测试工具,这里用的是BrowserStack
测试步骤1:选取浏览器和版本,这里对照上面的表格针对性测试几款浏览器的边界值版本
测试步骤2:拿到虚拟机跑出来的页面访问结果
测试步骤3:查看结果,白页为未访问到项目,最终与CanIUse给出的兼容表一致
(4)根据网站header入库的 navigator.userAgent 字段分析用户浏览器使用分布
字段参考:(容易区分的部分高亮)
IE6.0:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
IE7.0:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)
IE8.0:Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)
IE9.0:Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)
IE10:Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.3; WOW64; Trident/6.0) IE11:Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Chrome:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
firefox:Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0
Opera:Opera/9.80 (Windows NT 5.1; U; zh-cn) Presto/2.9.168 Version/11.50
360浏览器:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; 360se)
-
测试打包流程,发现vite2和@vitejs/plugin-legacy 库的@2版本不匹配
解决方案:降级@vitejs/plugin-legacy这个包@2版本到@1.8.2,即可
// vite.config.js import legacyPlugin from '@vitejs/plugin-legacy' // IE和旧版chrome兼容 legacyPlugin({ targets : [ 'ie >= 11' ] , additionalLegacyPolyfills : [ 'regenerator-runtime/runtime' ] })
-
兼容性测试结果
主流(支持):Chrome、Firefox、Safari、Edge
国内(支持):QQ浏览器、搜狗浏览器、猎豹浏览器、360浏览器
转载自:https://juejin.cn/post/7177328430122270776