89

Vue 升级小记

 6 years ago
source link: https://zhuanlan.zhihu.com/p/31436018?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Vue 升级小记

猫奴,Eleme Fe 其中一枚

Vue 升级小记

最近接手了一个 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: {
  productTags: function () {
    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 Routerapp,而只需要传一个路由属性给 Vue 实例,如下:

// Vue 1.x
router.map(routes)
router.start(Vue.extend({
  store,
  components: {
    app: require('./app.vue')
  }
}), 'body')
// Vue 2.x
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

3. 生命周期

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

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

4. transition

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

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

过渡CSS 变化

举个栗子:

// Vue 1.x :
<div v-if="isShow" transition="info-fade"> 
 <span> hello world ! </span>
</div>

.info-fade-transition {
  transition: all .3s ease;
}
.info-fade-enter, info-fade-leave {
  opacity: 0;
}
// Vue 2.x
<transition name="info-fade">
  <span v-if="isShow"> hello world ! </span>
</transition>

.info-fade-enter-active, info-fade-leave-active {
  transition: all .3s ease;
}
.info-fade-enter, info-fade-leave {
  opacity: 0;
}

Javascript 钩子

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

// Vue 1.x
Vue.transition('expand', {

  beforeEnter: function (el) {
    el.textContent = 'beforeEnter'
  },
  enter: function (el) {
    el.textContent = 'enter'
  },
 ...
})
// Vue 2.x
methods: {
  // 过渡进入
  // 设置过渡进入之前的组件状态
  beforeEnter: function (el) {
    // ...
  },
  // 设置过渡进入完成时的组件状态
  enter: function (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-elv-ref 合并成 ref 属性
  • 在 Vue 的实例中不能使用Vue.setVue.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,不受伤。。。

Vue 1.0 官方文档地址 Vue 2.0 官方文档地址

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK