依据vue官方文档,vue2在2023年12月31日终止维护。因此决定将原来的岁月云记账升级到vue3,预计工作量有点大,于是想着把过程记录下来。
原系统使用的技术栈
"dependencies": {
"axios": "^0.21.1",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-transform-remove-console": "^6.9.4",
"clipboard": "^2.0.6",
"core-js": "^3.6.5",
"echarts": "^5.0.1",
"element-china-area-data": "^5.0.2",
"element-ui": "^2.15.2",
"file-saver": "^2.0.5",
"js-cookie": "2.2.0",
"js-md5": "^0.7.3",
"jsencrypt": "^3.2.1",
"less": "^4.1.0",
"less-loader": "^7.2.1",
"moment": "^2.24.0",
"nprogress": "0.2.0",
"pinyin-match": "^1.2.2",
"postcss-plugin-px2rem": "^0.8.1",
"resize-detector": "^0.2.2",
"uuid": "^8.3.2",
"vue": "^2.6.11",
"vue-router": "^3.0.7",
"vuex": "3.0.1",
"xlsx": "^0.17.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"compression-webpack-plugin": "^5.0.0",
"crypto-js": "^4.1.1",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"file-loader": "^6.2.0",
"fingerprintjs2": "^2.1.4",
"image-webpack-loader": "^7.0.1",
"lodash-webpack-plugin": "^0.11.6",
"mini-css-extract-plugin": "^1.5.0",
"quill-image-extend-module": "^1.1.2",
"url-loader": "^4.1.1",
"vue-quill-editor": "^3.0.6",
"vue-template-compiler": "^2.6.11"
},
新系统使用的技术栈如下,可以看到至少有下面几点变化
- 代码编译从
webpack
调整为vite
- 前端界面框架从
element-ui
升级到element-plus
- 状态管理从
vuex
调整为pinia
- 支持
typescript
- 菜单框架调整为多页签模式
- api调整为动态加载,页面路径调整为动态加载。
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@tinymce/tinymce-vue": "5.0.0",
"axios": "^1.6.1",
"codemirror": "5.65.5",
"cropperjs": "^1.6.1",
"crypto-js": "4.1.1",
"docx-preview": "^0.3.0",
"echarts": "^5.4.3",
"element-china-area-data": "5.0.2",
"element-plus": "2.2.32",
"js-cookie": "^3.0.5",
"js-md5": "^0.8.3",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinyin-match": "1.2.2",
"resize-detector": "0.2.2",
"sortablejs": "^1.15.0",
"tinymce": "6.3.2",
"uuid": "^9.0.1",
"vue": "^3.3.8",
"vue-router": "^4.2.5",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@types/node": "18.11.11",
"@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"postcss-plugin-px2rem": "^0.8.1",
"tailwindcss": "^3.4.3",
"typescript": "^4.9.3",
"unplugin-auto-import": "^0.16.7",
"unplugin-icons": "^0.17.4",
"unplugin-vue-components": "^0.25.2",
"vite": "^3.2.3",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "3.2.0",
"vue-tsc": "^1.0.11"
}
1 界面相关
1.1 slot插槽
slot-scope="scope"
批量更改为#default="scope"
v-slot="scope"
统一调整为#default="scope"
1.2 jsx语法问题
在vue2开发项目中经常遇到一些动态的dom节点,升级到vue3中,提示The JSX syntax extension is not currently enabled
,这个问题也是比较头痛。
引入@vitejs/plugin-vue-jsx
,在vite.config.ts
中配置jsx
语法支持,
进入到对应的界面,添加lang="jsx"
在使用到标签,增加h函数
将原来的
render: (h, data) => {
return (<span>{(data.row.period != '' && data.row.period != null) ? data.row.period + '年' : ''}</span>)
}
调整为
render: (data) => {
return h(<span>{(data.row.period != '' && data.row.period != null) ? data.row.period + '年' : ''}</span>)
}
1.3 作废的引用
1.4 图片require引入的问题
例如下面的代码,会提示index.vue:129 Uncaught (in promise) ReferenceError: require is not defined
因为vite是基于 ES Modules
,因此不支持CommonJS
的require
写法
故而按照url: new URL('@/assets/home/balance.png',import.meta.url).href,
图片就可以正常加载了
2 api请求
看到下面这个结果真是头大,工程师是不是在摸鱼,复制、粘贴不用动脑,为何不写一个指令来实现。只能一个个改。这个里面的工作量非常之大,得耐心去调整。也可以借助Fitten Code
Ai插件,可以把重复的事情,自动联想出来,还是可以加速的
新的api
2.1 this.$store.dispatch
看看原来的store方式调用api,写法确实冗余拖拉,没有新的方式简洁
新方式,一句话定义完了,不用搞那么多绕绕。
2.2 直接应用api方法
有一些人并不是直接用dispatch
模式,例如这样的,是直接引用了api里面的方法
另外需要注意加上async
这种类型就有57处,看来代码走查非常有必要,当时做项目确实疏忽了这个事情,最后前端工程师离职了,吃亏的就是留下来的人。前提还得你懂前端,光靠前端技术经理,这个事情也是够呛。
2.3 axios请求
还有人在页面直接写了axios
调用,这些也需要统一调整过来,真是一个修行的过程,花了我一天的时间,将256处直接用axios的请求调整过来。
继续重构代码,发现还有这样打开界面的,通过window.open
直接打开界面,这样的代码都属于比较隐蔽的。
3 存储相关
可以参考我之前写的文档多租户平台前端存储结构的选择,工程师采用vuex存储状态,肯定不合理,结果就是多租户被架空。像这类问题估计99%的前端开发工程师面试一定能答上来,但是实际应用就只能呵呵了。
4 状态管理
因为使用的是pina
,因此下面的写法就失效了。mapState和
mapGetters`,因此这块的工作量也是比较大的
import {mapGetters, mapState} from "vuex";
computed: {
...mapState({
periodOptions: function (state) {
return state.accountSets.periodOptions
},
}),
...mapGetters([
'changeSubject',
'initTreeStatus',
'categoryTrees',
'allSubject',
'bindTime',
]),
},
因为pinia
中没有,故而需要调整。
5 Vite相关
服务起来,并不代表迁移的页面一定能运行,因为菜单都调整了,这个问题就不好排查了,因为不知道是那个地方会造成循环调用。
vue-router.js?v=e7f9366d:1117 Uncaught RangeError: Maximum call stack size exceeded
at [Symbol.replace] (<anonymous>)
at String.replace (<anonymous>)
at commonEncode (vue-router.js?v=e7f9366d:1117:31)
at encodeQueryValue (vue-router.js?v=e7f9366d:1123:10)
at encodeQueryKey (vue-router.js?v=e7f9366d:1126:10)
at stringifyQuery (vue-router.js?v=e7f9366d:1169:11)
at stringifyURL (vue-router.js?v=e7f9366d:73:35)
at resolve (vue-router.js?v=e7f9366d:2084:22)
at pushWithRedirect (vue-router.js?v=e7f9366d:2145:46)
at pushWithRedirect (vue-router.js?v=e7f9366d:2152:14)
遇到这个问题,不知道从哪里下手,调试pushWithRedirect
,发现有一个\
的路由,那么第6感告诉我,这个地方会不会因为这个路由导致的。
从代码可以看到/
在路由中,被重定向到sysConfig.DASHBOARD_URL
,那么如果这个sysConfig.DASHBOARD_URL
是\
,重定向后发生死循环,于是就出现了上面的错误。
5.1 全局组件
在vue3中写法稍微有差异,在vite
中使用完整路径
5.2 不支持require
vite不支持requre,因此下面的写法是有问题的
5.3 组件全路径
vite中引入组件是全路径,故而下面的写法要调整