文章目录
- 一、前言
- 二、Vue3 新特性
- 三、Vue2 安全保障
- 四、迁移方案
- 4.1 迁移辅助工具 @vue/compat 库
- 五、迁移注意事项
- 5.1 升级和兼容性
- 5.2 组件的改变
- 5.3 Composition API
- 5.4 Vue Router
- 5.5 Vuex
- 5.6 其他库和插件
- 5.7 差异示例
- 5.7.1 创建vue
- 5.7.2 定义时间格式全局过滤器
- 5.7.3 vue2自定义组件中使用到的修饰符sync
- 六、综述
- 七、拓展阅读
一、前言
1 月 2 日消息,Vue.js 作者尤雨溪发文声称,Vue 2 已于 2023 年 12 月 31 日结束生命周期(EOL),目前 Vue 2 已经无法接收到新功能及修复补丁,还在使用 Vue 2 的开发团队应迁移至最新的 Vue 3 版本。
Vue 2.0 最初发布于 2016 年,至今已经有 7 年历史,尤雨溪表示,2.0 版本是 Vue 成为主流框架历程中的一个重要里程碑,“不过随着 Vue 3 的成熟,如今是时候淘汰 Vue 2 了”。
根据公司战略要求,公司项目需进行升级改造,对于前端项目,计划由Vue2
升级至Vue3
,考虑到存量系统项目体量较大,故需结合两者特点制定可行性迁移方案。
二、Vue3 新特性
当前 Vue
最新稳定版本是 v3.4.21
。具有如下新特性:
- 增加代码可维护性
Vue2 使用的是options API
,代码逻辑比较分散,可读性差,可维护性差。Vue3 使用的composition API
逻辑分明,可维护性高,更友好的支持TS。在template
模板中支持多个根节点,支持jsx
语法。
笔者自身体验是独立使用 Vue2
开发应用项目的时候,不管怎么去组织代码,总是无法避免在 data
、template
、methods
中上下反复横跳,这种弊端在项目规模上来之后会更加明显。而且由于 vue-cli
是基于 Webpack
开发的,当项目规模上来后,每执行一下,调试环境就要 1 分钟时间甚至更长时间,这也是大部分复杂项目的痛点之一。
而 Vue3
的 Composition API
带来的代码组织方式更利于封装代码,维护起来也不会上下横跳。
但是,Vue3
也不是没有问题,由于新的响应式系统用了 Proxy
,会存在兼容性问题。也就是说,如果应用系统被要求兼容 IE11
,就应该选择 Vue2
。而且,Vue 团队也已经放弃 Vue 3
对 IE11 浏览器的支持。
其实,官方原来是有计划在 Vue3
中支持 IE11,但后来由于复杂度和优先级的问题,这个计划就搁置了下来。
不过,随着前端的快速发展,会发现浏览器和 JavaScript 本身已经有了巨大发展。大部分的前端项目都在直接使用现代的语言特性,而且微软本身也在抛弃 IE,转而推广 Edge
。所以 Vue 官方在重新思考后,决定让 Vue 3 全面拥抱未来,把原来准备投入到 Vue 3 上支持 IE11 的精力转投给 Vue 2.7
。
那么 Vue 2.7
会带来什么内容呢?
Vue 2.7
会移植 Vue3
的一些新特性,让开发者在 Vue2
的生态中,也能享受 Vue3
的部分新特性。在 Vue3
发布之前,Vue2
项目中就可以基于 @vue/composition-api
插件,使用 Composition API
语法,Vue2
会直接内置这个插件,在 Vue2
中默认也可以用 Compositon API
来组合代码。
-
提升页面渲染性能
Vue3 在更新DOM算法上,做了优化。在 Vue2 中,每次更新diff,都是全量对比,Vue3则只对比带有标记的,这样大大减少了非动态内容的对比消耗。 -
加强 MVVM 双向数据绑定效率
Vue2
双向数据绑定利用ES5
的Object.definePropert()
对对象属性进行劫持,结合发布订阅模式的方式来实现的。Vue3 中使用了es6
的ProxyAPI
对数据代理。
相比于vue2.x,使用proxy
的优势如下:
defineProperty
只能监听某个属性,不能对全对象监听;- 可以省去
for in
、闭包等内容来提升效率(直接绑定整个对象即可); - 可以监听数组,不用再去单独的对数组做特异性操作。
vue3.x
可以检测到数组内部数据元素变化。
- 项目可持续发展
Vue2
已于2023.12.31
停止维护,EOL之后,官方就不承诺修复和提供解答,现在继续用Vue2
存在一定的风险隐患。
三、Vue2 安全保障
对于Vue3
来说,并不是所有的Vue2
特性被废弃,例如选项式API仍然支持。但同时存在非兼容性改变,对于这部分非兼容性改变,升级迁移过程中就是不得不做的改造工作。关于非兼容性改造,详参官网文档《非兼容性(或称之为破坏性)改变》。
那么,是否必须由Vue2升级至Vue3呢?
对于暂时无法迁移的用户,尤雨溪表示,用户可以更新到 Vue 2 的最终版本(2.7.16
),或购买 Vue 2 的扩展支持,从而保证相关框架依然能够获得一定的安全性。
也就是说,如果不升级至Vue3,Vue2框架潜在的漏洞问题是无法得到修复的,存在一定的安全风险漏洞。当然,对于严重安全漏洞,Vue2还是会继续修复的。这也就不难理解为何规模稍微大点、管理审慎的公司会考虑做框架升级改造。
四、迁移方案
通过工具+手动升级
思路:
第一版可以先从基础进行迁移。完成框架整体升级到Vue3(可运行)之后可以进行细化。
步骤:
第一步: 先把Vue2
框架整体替换成 Vue3
,因为目前 Vue3
兼容 Options API
写法,这样代码结构可以先不用更改,后期可以逐步修改(因为涉及到所有页面和组件)。之后新增的页面和组件按照 Vue3
新增的 composition API
结构来写。
第二步: 把 Vue3
中已经不再支持的 API 和语法进行修改替换。包括 Vue3 已经不再支持过滤器filter
,v-model
用法也改变等。
第三步: 把项目使用到的第三方插件和UI框架(Element)替换成Vue3版本,对应用法可能也需要修改。可通过 package.json
进行检索修改。
对于这一步升级有以下几点比较麻烦:
- 项目中依赖库并不支持
vue3
; vue2
到vue3
的一些破坏性更改;- 项目中依赖组件库(
ElementUI
等)的破坏性更改; - 对一些新特性的尝试(
vite
,ts
,pinia
)等 公司项目迁移造成稳定性破坏(极其重要);
第四步: 确保项目代码语法编译无误后,需要检查代码中的业务是否正确,避免对公司项目迁移造成稳定性破坏(极其重要)。
第五步: 使用 TypeScript
重构 JS 代码,TypeScript
比 JavaScript
多了静态类型检查,也增加了一些新的语法,是给项目锦上添花。但是这一步会比较耗时(因为相当于修改把JS代码都要过一遍),但是项目中可以同时存在JS 和 TS,所以可以逐步替换。
使用自动化工具:
通过使用目前比较好用的开源工具可以完成第一步和第二步的转换(及框架转换、废弃属性检测),目前使用比较多的就是 gogocode。
GoGoCode 是一个基于
AST
的JavaScript/Typescript/HTML
代码转换工具,可以用它来构建一个代码转换程序自动化完成如框架升级、代码重构、多平台转换等工作。
当前 GoGoCode 支持解析和操作类型的代码为:JavaScript
(JSX)、Typescript
(TSX)、HTML
、Vue
。
对于工具改造代码,存在很多未知性,项目业务代码中有些比较复杂的(例如表单联动,规则校验等等)可能会对原来的逻辑有影响,需要逐一人工比对和测试。
4.1 迁移辅助工具 @vue/compat 库
使用自动化升级工具进行 Vue
的升级。
小项目不用多说,从 Vue2
升级到 Vue3
之后,对于语法的改变之处,挨个替换写法就可以。但对于复杂项目,需要借助自动化工具来帮我们过渡。
首先是在 Vue 3 的项目里,有一个 @vue/compat
的库,这是一个 Vue 3 的构建版本,提供了兼容 Vue 2 的行为。这个版本默认运行在 Vue 2 下,它的大部分 API 和 Vue 2 保持了一致。当使用那些在 Vue3
中发生变化或者废弃的特性时,这个版本会提出警告,从而避免兼容性问题的发生,帮助开发者很好地迁移项目。并且通过升级的提示信息,@vue/compat
还可以很好地帮助开发者学习版本之间的差异。
在下面的代码中,首先把项目依赖的 Vue 版本换成 Vue 3,并且引入 @vue/compat
"dependencies": {
- "vue": "^2.6.12",
+ "vue": "^3.2.19",
+ "@vue/compat": "^3.2.19"
...
},
"devDependencies": {
- "vue-template-compiler": "^2.6.12"
+ "@vue/compiler-sfc": "^3.2.19"
}
然后给 vue 设置别名 @vue/compat
,也就是以 compat 作为入口,代码如下:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.resolve.alias.set('vue', '@vue/compat')
......
}
}
这时就会在控制台看到很多警告,以及很多优化的建议。参照建议,挨个去做优化即可。
在 @vue/compat
提供了很多建议后,开发者需要慢慢做修改。但从另一个角度看,“偷懒”是优秀程序员的标志,社区就有能够做自动化替换的工具,比较好用的就是“阿里妈妈”出品的 gogocode,官方文档也写得很详细,就不在这里赘述了。
自动化替换工具的原理很简单,和 Vue
的 Compiler
优化的原理是一样的,也就是利用编译原理做代码替换。利用 babel 分析 Vue 2 的源码,解析成 AST
,然后根据 Vue 3 的写法对 AST
进行转换,最后生成新的 Vue 3 代码。
对于替换过程的中间编译成的 AST
,可以理解为用 JavaScript 的对象去描述这段代码,这和虚拟 DOM 的理念有一些相似,基于这个对象去做优化,最终映射生成新的 Vue 3 代码。
五、迁移注意事项
5.1 升级和兼容性
首先,需要确保项目依赖项都与Vue3
兼容。一些依赖库可能需要更新才能与Vue3
协同工作。在升级过程中,可能还需要进行一些代码调整以适应Vue3
的新变化。因此,在升级之前,建议先确保项目在最新版本的Vue2
中运行正常。
5.2 组件的改变
Vue3对组件系统进行了许多改进。现在,组件默认是按组合(Composition
)编写的,而不是按选项(Options
)。这意味着在Vue3
中,需要使用setup
函数来定义组件的状态和属性。如果正在将一个使用Vue2
的旧组件迁移到Vue3
,那么这将是一个重大的改变。当然,对于选项式API,Vue3目前还是支持的,后期是否废弃不得而知。
解决方法:
将setup
函数用于新创建的组件,以及需要使用组合API的任何现有组件。对于那些不需要组合API的组件,可以选择不使用setup
函数,并继续使用旧的选项API。
5.3 Composition API
Vue3
引入了组合API,它是一个更灵活、更强大的方式来组织和复用组件逻辑。然而,这并不意味着组合API可以完全替代选项API。在某些情况下,可能需要同时使用两种API。
解决方法:
对于需要使用组合API的组件,确保理解了它的工作方式,并正确地将其应用于项目代码。同时,对于那些仍然需要使用选项API的组件,确保正确地进行了转换。
5.4 Vue Router
Vue Router
是Vue.js的官方路由库。在Vue 3中,Vue Router
也得到了更新。如果正在使用Vue Router2,那么需要了解一些重要的改变。
解决方法:
确保阅读了Vue Router 4
的文档,并理解了新API的工作方式。特别是要注意createRouter
函数和新的导航守卫的变化。如果遇到了任何问题,可以查看Vue Router的迁移指南以获取帮助。
5.5 Vuex
Vuex
是Vue.js的状态管理库。如果正在使用Vuex 2
,那么你需要了解一些重要的改变。
解决方法:
确保阅读了Vuex 4
的文档,并理解了新API的工作方式。特别是要注意createStore
函数和新的模块系统的变化。如果遇到了任何问题,可以查看Vuex的迁移指南以获取帮助。
5.6 其他库和插件
如果项目还使用了其他库或插件,例如Vuetify
或vue-tables
,确保这些库或插件已经更新到与Vue3兼容的版本。
解决方法:
查看这些库或插件的文档,并检查是否已经发布了与Vue3兼容的版本。如果有必要,你可以考虑迁移到替代库或自行编写必要的功能。
总结:
从Vue2
迁移到Vue3
可能会遇到一些挑战,但只要了解了新版本的变化并采取正确的措施,就可以成功地进行迁移。在迁移过程中,保持耐心并持续测试你的代码以确保一切正常工作是非常重要的。
5.7 差异示例
了解一下 Vue3
不兼容的那些具体语法,除了可以帮助开发者在升级项目后,避免写的代码无法使用,还会让更好地适应 Vue3
。详细的兼容性变更,官方有一个迁移指南。
5.7.1 创建vue
首先,来看一下 Vue 2 和 Vue 3 在项目在启动上的不同之处。在 Vue 2 中,使用 new Vue()
来新建应用,有一些全局的配置会直接挂在 Vue 上,比如通过 Vue.use
来使用插件,通过 Vue.component
来注册全局组件,如下面代码所示:
Vue.component('el-counter', {
data(){
return {count: 1}
},
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
let VueRouter = require('vue-router')
Vue.use(VueRouter)
上述代码注册了一个 el-counter 组件,这个组件是全局可用的,它直接渲染一个按钮,并且在点击按钮的时候,按钮内的数字会累加。
然后注册路由插件,这也是 Vue 2 中使用 vue-router
的方式。这种形式虽然很直接,但是由于全局的 Vue 只有一个,所以当在一个页面的多个应用中独立使用 Vue 就会非常困难。
看下面这段代码,在 Vue 上先注册了一个组件 el-counter,然后创建了两个 Vue 的实例。这两个实例都自动都拥有了 el-couter 这个组件,但这样做很容易造成混淆。
Vue.component('el-counter',...)
new Vue({el:'#app1'})
new Vue({el:'#app2'})
为了解决这个问题,Vue 3 引入一个新的 API ,createApp
,来解决这个问题,也就是新增了 App 的概念。全局的组件、插件都独立地注册在这个 App 内部,很好的解决了上面提到的两个实例容易造成混淆的问题。下面的代码是使用 createApp 的简单示例:
const { createApp } = Vue
const app = createApp({})
app.component(...)
app.use(...)
app.mount('#app1')
const app2 = createApp({})
app2.mount('#app2')
createApp
还移除了很多常见的写法,比如在 createApp
中,就不再支持 filter
、on
、 off
、set
、delete
等 API。
在 Vue 3 中,v-model
的用法也有更改。其实 Vue 3 还有很多小细节的更新,比如 slot
和 slot-scope
两者实现了合并,而 directive
注册指令的 API 等也有变化。
vue2
写法 :
import Vue from 'vue'
vue3
写法 :
import { createApp } from 'vue' //引入vue的构造函数createApp ,官网推荐
import App from './App' // 引入主页面
const app = createApp(App)
以上3句才能真正创建vue。
5.7.2 定义时间格式全局过滤器
vue2
写法 :
Vue.filter('dateFormat', function (daraStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return moment(daraStr).format(pattern)
})
vue3
写法 :
// 定义时间格式全局过滤器
app.config.globalProperties.$filters = {
dateFormat(daraStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return moment(daraStr).format(pattern)}
}
5.7.3 vue2自定义组件中使用到的修饰符sync
例如::trees.sync
,:model.sync
,
v-model.sync
将表单元素的值与数据对象中的 username 属性进行双向绑定。当输入框的值发生变化时,username 属性的值也会自动更新;反之亦然。
解决办法: 直接去除sync
关键字即可
例如,vue2写法定义组件:
<treeItem v-for="(model,i) in treeDataSource"
:key="'root_node_'+i"
:root="0"
:num="i"
:nodes="treeDataSource.length"
:trees.sync="treeDataSource"
:model.sync="model" ></treeItem>
vue3
写法定义组件:
<treeItem v-for="(model, i) in treeDataSource"
:key="`root_node_${i}`"
:root="0"
:num="i"
:nodes="treeDataSource.length"
:trees="treeDataSource"
:model="model" ></treeItem>
六、综述
综上所述,首先,如果要开启一个新项目,那直接使用 Vue 3 是最佳选择。
Vue2升级至Vue3过程中,需要考虑如下要点:
- 依赖包升级
- 非兼容性改造
- 浏览器兼容性
七、拓展阅读
- Vue官网
- Vue3 迁移指南
- 《Vue3进阶(壹):初探 Vue3》
- 《Vue3进阶(贰):Vue3 新特性》