记一次vue2项目迁移至vue3经历
升级之前建议阅读下面这篇文章,会对升级有帮助
vue官方文档:vue3迁移指南
注:vue3是支持vue2的选项式API风格的
下面就进入升级的流程了
环境准备,如果node和npm的版本过低需要升级,我的环境如下:

依赖升级以及错误修复
升级之前项目采用的是vue2.6+ant-design-of-vue1.7+less+axios+webpack,因为项目采用的是vue-cli搭建的,所以第一步是升级vue-cli,通过 vue upgrade 命令升级到最新的 @vue/cli-service
在终端执行npm view vue versions --json.查看vue的历史版本,选择合适的vue3版本后更改package.json,删除node-module,执行yarn重新安装node-module包,安装完成之后安装与vue版本相同的@vue/compact,如果项目中存在vue-template-compiler的话,将其替换为@vue/compiler-sfc
启动项目,根据终端的报错信息来修改项目
2.1
ERROR ValidationError: Invalid options object. Ignore Plugin has been initialized using an options object that does not match the API schema.
- options should be one of these:
object { resourceRegExp, contextRegExp? } | object { checkResource }
Details:
* options misses the property 'resourceRegExp'. Should be:
RegExp
-> A RegExp to test the request against.
* options misses the property 'checkResource'. Should be:
function
-> A filter function for resource and context.
如果出现以上错误,修改vue.config.js中configureWebpack中 plugin的写法。
报错的写法:
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
正确的写法:
plugins: [
onew webpack.IgnorePlugin(
{
resourceRegExp:/^\.\/locale$/,
contextRegExp: /moment$/,
}
)
]
2.2
Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.
options has an unknown property ‘plugins’. These properties are valid:
object { postcssOptions?, execute?, sourceMap?, implementation? }
如果出现以上错误,是因为项目中采用了postcss-pxtorem 或者postcss-px2rem插件导致的运行报错,只需在vue.config.js中更改loaderOptions的写法就可以了
错误的写法:
loaderOptions: {
postcss: {
plugins: [
require('postcss-px2rem')({
remUnit: 100,
}),
],
},
},
正确的写法:
loaderOptions: {
postcss: {
postcssOptions:{
plugins: [
require('postcss-px2rem')({
remUnit:100
}),
]
}
}
}
2.3
Uncaught Error: Module build failed (from ./node_modules/css-loader/dist/cjs.js):
TypeError: node.getIterator is not a function
如果出现上述错误,是因为用了postcss-px2rem插件,该插件与vue3不兼容,可以卸载该插件,安装postcss-pxtorem, vue.config.js修改如下:
css: {
loaderOptions: {
postcss: {
postcssOptions: {
plugins: [
require('postcss-pxtorem')({
// remUnit: 100,
rootValue: 100,
// unitPrecision: 5,
propList: ['*'],
}),
],
},
},
},
},
2.4
Component name “xxx“ should always be multi-word vue/multi-word-component-names
升级为vue3后,文件的命名不符合规范,有2种解决方式:
一是修改组件文件的命名
另一种是更改eslint验证规则,内容如下
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential',
'eslint:recommended'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
// 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
//在rules中添加自定义规则
//关闭组件命名规则
"vue/multi-word-component-names":"off",
"vue/no-v-model-argument":"off"
},
// overrides: [
// {
// files: [
// '**/__tests__/*.{j,t}s?(x)',
// '**/tests/unit/**/*.spec.{j,t}s?(x)'
// ],
// env: {
// jest: true
// }
// }
// ]
}
到这里运行项目基本就不会报错了
3.接下来就是修改main.js
原来的写法:
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app')
新的写法:
import {createApp} from 'vue'
const app = createApp(App)
app.use(store)
app.use(router)
app.use(Antd)
app.use(Plugins)
app.config.productionTip = false;
app.config.globalProperties.$message = message;
app.config.globalProperties.$filters = filters
app.mount('#app')
需要注意的是app.use()的使用顺序
更改完之后升级vue-router,vuex到最新的版本,然后升级UIant-design,综合所有原因,UI框架最终选择了版本2的,ant-design升级建议了解下这篇文章
原来vue-router的写法:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
function initRouter(isAsync) {
const options = isAsync
? require('./async/config.async').default
: require('./config').default
formatRoutes(options.routes)
// 路由默认模式
options.mode = 'history'
return new Router(options)
}
升级后的写法:
import { createRouter, createWebHistory } from "vue-router";
function initRouter(isAsync) {
const options = isAsync
? require('./async/config.async').default
: require('./config').default
formatRoutes(options.routes)
options.history = createWebHistory()
return createRouter(options)
}
原来vuex的写法:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
const store = new Vuex.Store({ modules })
升级后的写法:
import { createStore } from 'vuex'
import modules from './modules'
const store = createStore({ modules })
export default store
4.更改之后运行项目,可以看到控制台有非常多的警告
之后就是根据警告来修改代码
4.1
Vue.extend is not a function
如果出现上述错误,有可能是插件不兼容,可以根据提示找到相应的文档暂时注释该插件的使用,后续再放开,升级插件或者是找到可更换的插件
4.2
Non-function value encountered for default slot. Prefer function slots for better performance.
vue3 更改了h函数的语法。原来的h函数可以从render 函数中获取,但是vue3当中获取h函数的方法改成了 import { h } from ‘vue’.并且语法改成了h(节点的标签名,节点的属性(如果没有属性,可以用{}代替),节点标签的内容(注意这块改成了对象)),详情可以参考这篇文章
4.3
Extraneous non-props attributes (style) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.
原因:在组件的节点上添加了样式
解决办法:在组件的外面套一层标签,把组件的样式放到这个标签上
4.4 Invalid VNode type: undefined (undefined)
原因:某个节点获取不到,我这边是动态引入组件,()=>import(‘@/components/tag/index.vue’) 报的警告
解决方式:vue3 增加了一个动态注册组件的方法defineAsyncComponent,用这个方法包裹一下就可以啦
4.5
Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with `markRaw` or using `shallowRef` instead of `ref`.
原因:组件是一个响应对象,将会进一步引入性能开销,也是解决警告5后出现的
解决方法:用markRaw包裹一下,写法如下:
markRaw(defineAsyncComponent(()=> import (‘组件路径’)))
解决完上面的问题,项目基本上就可以正常的运行啦