前言

前段时间,公司准备在现有的 Vue2 项目中做一个聊天系统,但考虑开发聊天系统的周期并不短,客户又急需。于是准备对接腾讯的 IM 组件,来实现。

不知道的这里贴个官网: IM TUIKit 官方文档

对于 TUIKit,官方文档上有以下要求:

然而我们公司项目是使用 Vue2 版本的,这显然不符合要求。如果要对接,那么必然要升级项目为Vue3 版本。

思考

首先要明白,Vue2 升级 Vue3 能带来什么?

可以参看一下这篇博客:
应不应该从 Vue 2 升级到 Vue 3

总结一下下列几点:

Vue3 优点:

1. 增加了代码的可维护性

Vue2 使用的是 options 的API ,代码逻辑比较分散,可读性差,可维护性差。Vue3 使用的是 compositionAPI 逻辑分明,可维护性高,更友好的支持TS。在 template 模板中支持多个根节点,支持jsx语法。

2. 提升了页面渲染性能

Vue3 在更新DOM算法上,做了优化。在 Vue2 中,每次更新diff,都是全量对比,Vue3则只对比带有标记的,这样大大减少了非动态内容的对比消耗。

3. 加强了 MVVM 双向数据绑定的效率

Vue2 的双向数据绑定是利用 ES5 的 Object.definePropert() 对对象属性进行劫持,结合 发布订阅模式的方式来实现的。Vue3 中使用了 es6 的 ProxyAPI 对数据代理。
相比于vue2.x,使用proxy的优势如下:

  1. defineProperty只能监听某个属性,不能对全对象监听
  2. 可以省去for in、闭包等内容来提升效率(直接绑定整个对象即可)
  3. 可以监听数组,不用再去单独的对数组做特异性操作 vue3.x 可以检测到数组内部数据的变化

4. 项目可持续发展

Vue 2官方还会再维护两年,但两年后的问题和需求,官方就不承诺修复和提供解答了,Vue 3 则不会。

当然,还有其他的,这里只是列出几个主要的。

升级存在的隐患

  1. 新的响应式系统用了 Proxy,会存在兼容性问题(不支持IE)。
  2. 框架底层进行了大量重构,新增和删除了很多原来的API,代码结构也发生了变化。很多地方需要进行破坏性修改,从而容易导致各种问题的出现。
  3. 项目使用到的第三方插件和 UI框架(Element)也需要替换和更改成 对应Vue3可用版本。

升级方式

通过工具+手动升级

思路:

第一版可以先从基础进行迁移。完成框架整体升级到Vue3(可运行)之后可以进行细化。

步骤:

第一步: 先把Vue2 框架整体替换成 Vue3,因为目前 Vue3也是兼容了 Options 写法,这样代码结构可以先不用更改,后期可以逐步修改(因为涉及到所有页面和组件)。之后新增的页面和组件按照 Vue3 新增的 compositionAPI 结构来写。

第二步: 把 Vue3 中已经不再支持的 API 和语法进行修改替换。包括 Vue3 已经不再支持过滤器filter,v-model 用法也改变等。

第三步: 把项目使用到的第三方插件和UI框架(Element)替换成Vue3版本,对应用法可能也需要修改。需要通过 package.json 里注册目录,在页面进行检索修改。

对于这一步升级有以下几点比较麻烦:

  1. 项目中依赖的库并不支持vue3
  2. vue2到vue3的一些破坏性更改
  3. 项目中依赖组件库(ElementUI等)的破坏性更改
  4. 对一些新特性的尝试(vite,ts,pinia)等 公司项目迁移造成稳定性破坏(极其重要)

第四步: 确保项目代码语法编译无误后,需要检查代码中的业务是否正确,避免对公司项目迁移造成稳定性破坏(极其重要)。

第五步: 使用 TypeScript 重构 JS 代码,TypeScript 比 JavaScript 多了静态类型检查,也增加了一些新的语法,是给项目锦上添花。但是这一步会比较耗时(因为相当于修改把JS代码都要过一遍),但是项目中可以同时存在JS 和 TS,所以可以逐步替换。

使用自动化工具:
通过使用目前比较好用的开源工具可以完成第一步和第二步的转换,目前使用比较多的就是 gogocode:文档

对于工具改造代码,存在很多未知性,项目业务代码中有些比较复杂的(例如表单联动,规则校验等等)可能会对原来的逻辑有影响,需要逐一人工比对和测试。

实施

1. 创建一个新的 vue3 + TS 项目,里面安装完相关基础依赖

整个项目重新搭建,使用了 Vue3 作为技术框架。使用了 Vite 作为打包和构建工具(代替了之前 Vue2 的 Webpack 构建工具),因为 Vite 拥有更好的打包编译性能。

增加了 TypeScript 依赖,可以作为 TS 代码编写编译,增强了项目可持续维护性(之后可转成全TS)。使用 VueRouter 4.1.1 版本作为项目页面路由控制,此版本为 Vue3 配套版本。使用了 Pinia 代替了 Vuex 作为统一状态管理模块,因为 Pinia 与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。Api请求模块仍然采用了易用、简洁且高效的http库 Axios。使用 Sass 作为 CSS 的预编译语言,增强 CSS 的灵活性。使用升级版 Element Plus 作为界面 UI 框架。

技术栈

Vue3

项目使用的是 Vue ^3.2.36 版本。
Vue3 官网及其介绍:https://v3.cn.vuejs.org/guide/introduction.html

TypeScript

TypeScript 官网文档:https://www.tslang.cn/

VueRouter

VueRouter 官网及其介绍: https://router.vuejs.org/zh/

Pinia

Pinia 官网及其介绍:https://pinia.web3doc.top/

Axios

Axios 官网及其介绍:http://www.axios-js.com/

Sass

Sass 官网及其介绍:https://www.sass.hk/

Element Plus

Element Plus 官网及其介绍:https://element-plus.gitee.io/zh-CN/guide/design.html

代码规范

使用 EditorConfig + Prettier + ESLint 组合来实现代码规范化。

这样做带来好处:

  1. 解决团队之间代码不规范导致的可读性差和可维护性差的问题。
  2. 解决团队成员不同编辑器导致的编码规范不统一问题。
  3. 提前发现代码风格问题,给出对应规范提示,及时修复。 减少代码审查过程中反反复复的修改过程,节约时间。
  4. 自动格式化,统一编码风格,从此和脏乱差的代码说再见。

VSCode 编辑器 需要去插件市场下载插件 EditorConfig for VS Code 、Prettier – Code formatter、ESLint。
JetBrains 系列(WebStorm、IntelliJ IDEA 等)则不用额外安装插件,可直接使用 EditorConfig 配置。

构建工具

Vite

项目使用的是 Vite ^2.9.9 版本。
Vite 官网及其介绍 : https://vitejs.cn/

运行要求

Vue 版本:3.0以上
node 版本:Vite 需要 Node.js 版本 >= 12.0.0
浏览器:非IE浏览器

项目目录结构

src│  App.vue                               项目根节点│  env.d.ts                                类型声明文件│  main.ts                                 项目入口文件│  ├─api                                       项目请求封装复用统一管理目录│  │  index.js│  └─request                           请求封装存放目录│  │  │  └─modules                         项目请求模块目录│                            ├─assets                                  项目静态资源目录│  ├─css                                  CSS 存放目录│  │      │  ├─iconfont                         字体文件存放目录│  │      │  ├─images                           图片存放目录│  │      │  └─js                                    第三方JS库存放目录│      ├─common                             项目通用文件存放目录│  ├─enums                            通用枚举存放目录│  ├─keysManage                   通用Key管理目录│  │      localKeys.ts│  │      sessionKeys.ts│  │      │  └─style                               sass 预编译通用样式存放目录├─components                       公共组件存放目录│  │                                       │  ├─base                                基础组件存放目录│  │      │  ├─business                         业务组件存放目录│  │      │  ├─function                          功能组件存放目录│  │      │  └─template                          模板组件存放目录│      ├─config                                   项目通用配置文件目录│      index.dev.js│      index.exportdata.js│      index.js│      index.prod.js│      index.uat.js│      ├─router                                   项目路由存放目录│  │  index.ts│  │  │  └─modules                            路由模块化目录├─store                                      公共状态管理存放目录│  │  index.js│  │  │  └─modules                            公共状态管理模块化│          login.js├─ws                                          WebSocket 模块目录│  ├─mixins                                     公共提取方法目录│ ├─h5                                          移动端页面(多入口)│          ├─util                                        通用工具函数存放目录└─views                                      项目页面存放目录    ├─modules                             页面模块化目录    └─other

关于语法和Api的改变可以参见Vue官方文档:Vue2 迁移

全局 API

  1. 全局 Vue API 已更改为使用应用程序实例
  2. 全局和内部 API 已经被重构为支持 tree-shake

模板指令

  1. 组件上 v-model 用法已更改,以替换 v-bind.sync
  2. 和非 v-for 节点上的 key用法已更改
  3. 在同一元素上使用的 v-if 和 v-for 优先级已更改
  4. v-bind=“object” 现在排序敏感
  5. v-on:event.native 修饰符已移除
  6. v-for 中的 ref 不再注册 ref 数组

组件

  1. 只能使用普通函数创建函数式组件
  2. functional attribute 在单文件组件 (SFC) 的 和 functional 组件选项中被废弃
  3. 异步组件现在需要使用 defineAsyncComponent 方法来创建
  4. 组件事件现在需要在 emits 选项中声明

渲染函数

  1. 渲染函数 API 更改
  2. $scopedSlots property 已移除,所有插槽都通过 $slots 作为函数暴露
  3. $listeners 被移除或整合到 $attrs
  4. $attrs 现在包含 class 和 style attribute

自定义元素

  1. 自定义元素检测现在在模板编译时执行
  2. 特殊的 is attribute 的使用被严格限制在被保留的 标签中

其他小改变

  1. destroyed 生命周期选项被重命名为 unmounted
  2. beforeDestroy 生命周期选项被重命名为 beforeUnmount
  3. default prop 工厂函数不再可以访问 this 上下文
  4. 自定义指令的 API 已更改为与组件生命周期一致,且 binding.expression 已移除
  5. data 选项应始终被声明为一个函数
  6. 来自 mixin 的 data 选项现在为浅合并
  7. Attribute 强制策略已更改
  8. 一些过渡的 class 被重命名
  9. 不再默认渲染包裹元素
  10. 当侦听一个数组时,只有当数组被替换时,回调才会触发,如果需要在变更时触发,则必须指定 deep 选项
  11. 没有特殊指令的标记 (v-if/else-if/else、v-for 或 v-slot) 的 现在被视为普通元素,并将渲染为原生的 元素,而不是渲染其内部内容。
  12. 已挂载的应用不会取代它所挂载的元素
  13. 生命周期的 hook: 事件前缀改为 vnode-

被移除的 API

  1. keyCode 作为 v-on 修饰符的支持
  2. o n 、 on、 onoff 和 $once 实例方法
  3. 过滤器 (filter)
  4. 内联模板 attribute
  5. $children 实例 property
  6. propsData 选项
  7. $destroy 实例方法。用户不应再手动管理单个 Vue 组件的生命周期。
  8. 全局函数 set 和 delete 以及实例方法 $set 和 $delete。基于代理的变化检测已经不再需要它们了。

Element 和 Element Plus 框架也有所修改,请自行查阅 Element Plus 文档,对组件用法进行调整。

历时

可能你会比较关系项目升级的耗时,以我公司项目为例,整个 Vue2 项目大概 200 多个页面,包括40多个表单。整个前端小组,5个人历时一个半月初步完成第一版,时间还是有点赶,主要在升级过程中不能开发新需求,怕积压了太多客户需求。上线后总体没有太多大问题,但是避免不了一些小问题,毕竟是整个项目的迁移,有问题马上解决就行了。不要耽误用户的时间。大概就这么多,感谢阅读!