Vue2 大型项目升级 Vue3 详细经验总结

Posted 蜗牛不会飞_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue2 大型项目升级 Vue3 详细经验总结相关的知识,希望对你有一定的参考价值。

前言

前段时间,公司准备在现有的 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. <template v-for> 和非 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) 的 <template>和 functional 组件选项中被废弃
  3. 异步组件现在需要使用 defineAsyncComponent 方法来创建
  4. 组件事件现在需要在 emits 选项中声明

渲染函数

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

自定义元素

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

其他小改变

  1. destroyed 生命周期选项被重命名为 unmounted
  2. beforeDestroy 生命周期选项被重命名为 beforeUnmount
  3. default prop 工厂函数不再可以访问 this 上下文
  4. 自定义指令的 API 已更改为与组件生命周期一致,且 binding.expression 已移除
  5. data 选项应始终被声明为一个函数
  6. 来自 mixin 的 data 选项现在为浅合并
  7. Attribute 强制策略已更改
  8. 一些过渡的 class 被重命名
  9. <TransitionGroup> 不再默认渲染包裹元素
  10. 当侦听一个数组时,只有当数组被替换时,回调才会触发,如果需要在变更时触发,则必须指定 deep 选项
  11. 没有特殊指令的标记 (v-if/else-if/else、v-for 或 v-slot) 的 <template> 现在被视为普通元素,并将渲染为原生的 <template> 元素,而不是渲染其内部内容。
  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个人历时一个半月初步完成第一版,时间还是有点赶,主要在升级过程中不能开发新需求,怕积压了太多客户需求。上线后总体没有太多大问题,但是避免不了一些小问题,毕竟是整个项目的迁移,有问题马上解决就行了。不要耽误用户的时间。大概就这么多,感谢阅读!

Vue.js升级小记

最近接手了一个 Vue 1.0 的陈年老项目,需要将其升级到Vue 2.0。下面记录一下升级过程:


安装迁移工具


首先需要安装 vue-migration-helper CLI 工具:


  1. 控制台运行命令:npm install --global vue-migration-helperCLI 工具来帮助项目从Vue 1.x 迁移到 2.x。 它扫描文件以查找特定于 Vue 的代码,并对需要升级的代码提供详细的警告。 vue-migration-helper的介绍说明告诉我们它大概能捕获 80% 的升级帮助信息,而不是全部。所以终端输出的帮助信息并不是完全正确的,在升级时不要盲目copy & paste,还是要根据实际情况去改写。

  2. 进入当前的项目:运行: vue-migration-helper


工具识别出了108 个需要升级的点:



由于这个古董项目不是用 Vue-CLI 构建的,为了避免在升级依赖上出错,我直接新起了一个Vue-CLI 项目,将老项目中的业务部分进行迁移,这个是最快最能避免踩坑的解决方案(当然,这么做是因为踩坑了,所以爬起来了=_=)与当前最新的Vue-CLI 的依赖版本做了对比:


  • "vue": "^1.0.24" to "vue": "^2.5.2"

  • "vue-router": "^0.7.13" to "vue-router": "^3.0.1"

  • "vuex": "^2.4.0" to "vuex": "^3.0.1"

  • "webpack": "^2.4.1" to "webpack": "^3.6.0"

  • "vue-loader": "^8.5.2" to "vue-loader": "^13.3.0"


升级代码


对于108 个需要升级的点来说,花费的时间远比想象的要多,除了根据官方文档进行迁移升级之外,运行项目还是会有很多报错,下面总结了一下改动比较多的地方。


1. 过滤器


移除内置过滤器


Vue 2.0 不再提供内置过滤器。可以创建自己的过滤器或者引入外部库如moment.js,accounting.js 来对时间和货币等进行格式化。之前项目中用到的 orderBy 方法已经被弃用了,根据升级指南的建议,直接引入了 lodash 工具库,并使用计算属性重构。


// Vue 1.x

<div v-for="tag in productTags | orderBy 'location'">

  {{ tag.tagTitle }}

</div>

// Vue 2.x

<div v-for="tag in productTags">

  {{ tag.tagTitle }}

</div>

 

import orderBy from 'lodash/orderBy'

...

...

...

computed{

  productTagsfunction () {

    return orderBy(this.tags, 'location')

  }

}

...


过滤器参数符号变更


// Vue 1.x

<div class="time">{{ item.appDate | date 'yyyy-MM-dd' }}</div>

 

// Vue 2.x

<div class="time">{{ item.appDate | date('yyyy-MM-dd') }}</div>


2. Vue Router


vue-router 的改动是相对来说非常大的,大部分都可以参考 官方文档 去修改,如:


  • router.go() 改成了router.push()

  • router.map() 被废弃,使用routes 选项数组

  • 使用 router-link 替换了 v-link

  • route.refresh 改成了 route.meta.refresh


对于beforeEach 来说现在是异步工作的,并且携带一个 next函数作为其第三个参数,beforeEach 经常用来设置页面的title,而 Vue 2.0 to 函数的使用也有一些改变,如下:


// Vue 1.x

router.beforeEach(({ to, next }) => {

  if (to.title) {

    ...

  }

  next()

})

// Vue 2.x

router.beforeEach((to, from, next) => {

  if (to.meta.title) {

   ...

  }

  next()

})


对于路由挂载根实例的改动,Vue 2.0 不再会有一个特殊的 API 用来初始化包含 Vue Router 的 app,而只需要传一个路由属性给 Vue 实例,如下:


// Vue 1.x

router.map(routes)

router.start(Vue.extend({

  store,

  components{

    apprequire('./app.vue')

  }

}), 'body')

// Vue 2.x

new Vue({

  el'#app',

  router,

  store,

  template'',

  components{ App }

})


3. 生命周期


生命周期钩子也是这次升级中比较大的改进点,对照 vue1.0文档 和 vue2.0 文档 ,异同如下表格:


Vue.js升级小记


因此主要的改动点是:使用mounted 钩子函数替换ready 钩子函数


4. transition


Vue 2.0 对动画做了非常大的更新,原来的transition属性已经被彻底废弃掉了,而使用 transition 或 来包裹元素去实现过渡效果,项目中关于动画的代码都要重新写,包括 CSS、 HTML ,还有 Javascript 钩子函数的改变。


在要升级的这个项目中,关于transition 的升级差不多有10多个,其中大部分的改动都可以按照迁移工具去copy & paste, 但是还是要清楚其中的异同,再去做修改。不然就会出现,错误提示消失了,但是动画不生效的情况。


过渡CSS 变化


Vue.js升级小记


举个栗子:


// Vue 1.x :

div v-if="isShow" transition="info-fade">

spanhello world ! /span>

/div>

 

.info-fade-transition {

  transitionall .3s ease;

}

.info-fade-enter, info-fade-leave {

  opacity0;

}

// Vue 2.x

transition name="info-fade">

  span v-if="isShow"hello world ! /span>

/transition>

 

.info-fade-enter-active, info-fade-leave-active {

  transitionall .3s ease;

}

.info-fade-enter, info-fade-leave {

  opacity0;

}


Javascript 钩子


Vue 2.0 transitions能够通过组件应用,它们不再只是一种单独类型,因此全局的Vue.transition()方法和transition配置都被丢弃。现在可以通过组件的属性和方法配置内嵌的过渡:


// Vue 1.x

Vue.transition('expand', {

 

  beforeEnterfunction (el) {

    el.textContent = 'beforeEnter'

  },

  enterfunction (el) {

    el.textContent = 'enter'

  },

...

})

// Vue 2.x

methods{

  // 过渡进入

  // 设置过渡进入之前的组件状态

  beforeEnterfunction (el) {

    // ...

  },

  // 设置过渡进入完成时的组件状态

  enterfunction (el, done) {

    // ...

    done()

  },

  ...

}


5. Class 与 Style 绑定


// Vue 1.x

<div class="qa-item-question {{item.viewStatus === 1 ? 'isread' : ''}}"> </div>


如果使用 vue-migration-helper工具,它会提示你将老的代码替换成这样:


// Vue 2.x

<div v-bind:class="'qa-item-question ' + item.viewStatus === 1 ? 'isread' : ''"></div>


然而由于运算符优先级问题,最后的结果可能会是 :


<div class=""></div>


因此,应该注意的不能完全依赖升级工具的提醒去直接 copy & paste。


6. 双向数据绑定


Replace :visible.sync="xxx" with :visible="xxx", then $emit an event from the child component to trigger an update to xxx in the parent


Vue 2.x 中,为了规范数据流动,砍掉了 .sync,用来阻止子组件影响父组件所绑定的值,因为.sync 破坏了单向数据流。但是很多情况下还是会需要双向绑定的,比如 dialog 弹窗,当关闭时,将此状态返回给父组件。


而 Vue 2.x 中,子组件只能被动接收父组件传递过来的数据,并在子组件内不能修改由父组件传递过来的props数据。每次父组件更新时,子组件的所有prop都会更新为最新值,因此不应该在子组件内部改变 prop,如果我们尝试直接修改prop 属性的值,就会有警告提示:


[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: 

"xxx" (found in component ) 


Vue2.0 的官方文档也给出了解决方案:


定义一个局部变量,并用 prop 的值初始化它

定义一个计算属性,处理 prop 的值并返回


而对于 .sync ,Vue 2.3 + v-bind 指令中将其引进,成为一枚语法糖,它会被扩展为一个自动更新父组件属性的 v-on 监听器。


7. $loadingRouteData


Vue 2.0 移除了 $loadingRouteData 钩子。如果我们需要判断页面数据是否加载完成,需要自定义一个属性(例如: isLoading)


8. 其他简单的改动


  • v-for

    • track-by to :key

    • 废弃了$index

  • HTML 的计算插值{{{ foo }}}已经移除,取代的是v-html指令

  • v-el 和 v-ref 合并成 ref 属性

  • 在 Vue 的实例中不能使用Vue.set 和 Vue.delete

  • 使用@click.native 监听根元素的原生事件,@click传的只是一个方法

  • 废弃了 Array.prototype.$set/$remove,用Vue.set 或者 Array.prototype.splice取代


9. 其他报错


浏览器报错:


[Vue warn]: Do not use built-in or reserved HTML elements as component id: dialog


因为dialog在 HTML5 里面是个原生的标签解决方法:重命名components里面组件的名称


总结


上面只是简单梳理了一下该项目在升级时遇到的一些问题,但并不是所有。单单是升级webpack,其实要修改的点就有很多,但是对于陈年的经过 N 手的老项目来说,并不适合直接升级,可能会造成项目更加混乱,因此这里走了一点捷径,绕过了很多升级 webpack 会遇到的坑。升级遇到的大部分问题官方文档都有详细的描述,遇到问题,沉着冷静别惊慌,我们的目标是:远离 bug,不受伤。。。

原文:https://zhuanlan.zhihu.com/p/31436018

广告

Vue.js升级小记


  • 回复“面试题”“ajax”等关键词,可查看前端面试题

  • 其它功能正在完善,不定期更新....


觉得本文对你有帮助?请分享给更多人

关注「」,提升前端技能

以上是关于Vue2 大型项目升级 Vue3 详细经验总结的主要内容,如果未能解决你的问题,请参考以下文章

Vue2.7正式发布,终于可以在Vue2项目中使用Vue3的特性了,真香~

将项目从 Vue2 升级到 Vue3 的最佳方法

H5活动开发问题总结

Vue2.7升级指南,享受Vue3新特性

Vue2.7升级指南,享受Vue3新特性

记一次 Vue2 迁移 Vue3 的实践总结