Vue.js 0.10 -> 0.11 升级指南
发布在Vue.js 中文入门2014年12月4日view:11637
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

Vue 0.11 在内部实现上有很多巨大的改动,API 也有一些不小的变化,但整体风格依然保持一致。假如你还在用 0.10,希望这篇文章能帮你无痛过渡到最新版本。

vm 创建流程

在 0.10 中,如果你创建一个 vm 的时候不给 el 选项,Vue 会自动帮你创建一个空的 div。在 0.11 里面就不再是这样了,如果不给 el 的话,vm 会处于 “未加载” 状态,此时你可以继续对 vm 的选项和数据做调整,直到你调用新的 vm.$mount(el) 方法。如果 $mount() 被调用时没有给参数,依然会自动创建一个 div

数据作用域继承

在 0.11 里,所有的子组件默认是独立作用域(除了 v-repeat 创建的匿名实例外),也就是说在子组件的模板里不能直接引用父作用域的属性。推荐的做法是通过 v-with 或是 paramAttributes 来明确地向子组件传递需要传递的数据。这样设计的目的是鼓励组件之间更好的解耦。

paramAttributes 作为通用的组件接口可以让你的模板看上去更像 web components:

Vue.component('user-profile', {
  paramAttributes: ['user'],
  template: '<span>{{user.name}}</span> <span>{{user.email}}</span>'
})
<user-profile user="{{userData}}"></user-profile>

但如果你还是想要自动继承父作用域的所有属性,则可以用新的 inherit: true 选项来实现。启用 inherit: true 的组件会用类似 Angular scope 一样的原型继承方式继承父 vm 上的所有属性。

vm 选项改动

  • eldata 作为组件定义选项

    当进行组件定义的时候,如果 data 里包含对象,那么这同一个对象会被所有创建的实例共享,这显然不是我们想要的。el 选项也是如此,我们不希望所有的组件实例共享同一个 DOM 元素。因此在 0.11 里,在 eldata 作为组件定义选项(而不是实例化选项)的时候,必须提供一个返回初始值的函数:

  var MyComponent = Vue.extend({
    el: function () {
      var el = document.createElement('p')
      // 初始化元素
      el.className = 'content'
      return el
    },
    data: function () {
      // 初始化默认数据
      // 多次 extend 时,多个 data 函数返回的值会被合并成一个对象
      return {
        a: {
          b: 123
        }
      }
    }
  })
  • paramAttributes 选项对于带 - 的属性的处理

    当你用一个带短横的属性,比如 my-param 作为 paramAttribute 的时候,在模板里调用这个属性可能会遇到问题,因为表达式 parser 会认为这是一个减法表达式。所以 Vue 会自动把它转化为驼峰命名,也就是 this.myParam。另外用 data- 开头的属性,Vue 会自动去掉 data 前缀,所以 data-user 会被直接转化为 this.user

  • 新选项:events, watch

    自动创建 $on 和 $watch 回调,不用多说了吧,看代码就明白了:

var vm = new Vue({
  data: {
    a: 1
  },
  events: {
    'hook:created': function () {
      console.log('created!')
    },
    greeting: function (msg) {
      console.log(msg)
    },
    // can also use a string for methods
    bye: 'sayGoodbye'
  },
  watch: {
    a: function (newVal, oldVal) {
      console.log('a changed from ' + oldVal + ' to ' + newVal)
    }
  },
  methods: {
    sayGoodbye: function () {
      console.log('goodbye!')
    }
  }
})
// -> created!
vm.$emit('greeting', 'hi!')
// -> hi!
vm.$emit('bye')
// -> goodbye!
vm.a = 2
// -> a changed from 1 to 2
  • 新选项:mixins

    mixins 选项是一个包含了多个 mixin 对象的数组。每个 mixin 对象都可以包含任意选项,Vue 会把所有的 mixin 对象和包含这些 mixin 的选项对象合并成一个最终的选项对象。通过 mixins 可以灵活地在多个组件之间共享特性。

var mixin = {
  created: function () { console.log(1) }
}
var vm = new Vue({
  mixins: [mixin],
  created: function () { console.log(2) }
})
// -> 1
// -> 2
  • 新选项:name

    这个选项完全是为了方便 debug,可以定义在 console 里 log vm 实例的时候输出的类名 (所以这个选项只能用在 Vue.extend 或者组件定义里):

var C = Vue.extend({
  name: 'MyComponent'
})
var instance = new C()
console.log(instance) // -> MyComponent { $el: ... }
  • 移除的选项:
    • parent (请使用新的 vm.$addChild() 方法)
    • lazy (请使用新的 <input v-model="abc" lazy> 语法)
    • id, tagName, className, attributes (请在 el 选项的函数里定义这些)

组件生命周期 hook 改动

  • created 现在在初始的数据观察完成后触发。要添加更多被观察的属性需要用 this.$add(key, val)
  • ready 现在在 vm 的 DOM 编译完毕并且 第一次被插入 document 后触发。如果已经在 document 里了会在编译后立刻触发。换句话说在 ready hook 里可以对元素的尺寸进行测量。
  • 新的 hook:compiled 会在 DOM 编译完毕后立刻触发,无论是否在 document 里。
  • 新的 hook: beforeCompile,在 DOM 编译开始之前触发。假如你没有提供 el 选项,则会在 $mount() 的时候才触发。
  • afterDestroy 改名为 destroyed

实例方法改动

$watch

  • 现在可以 $watch 一个表达式:
vm.$watch('a + b', function (newVal, oldVal) {
  // do something
})
  • 现在默认是 shallow watch,需要观察对象的内部属性变化需要传入第三个参数 deep: true
vm.$watch('someObject', callback, true)
vm.someObject.nestedValue = 123
// callback is fired
  • 默认只有当数据变了才会触发回调。但也可以用第四个参数在创建 watcher 的同时立刻触发一次回调:
vm.$watch('a', callback, false, true)
// callback is fired immediately with current value of `a`
  • 移除了 $unwatch 方法。每个 $watch() 调用会返回一个单独的 unwatch 函数:
var unwatch = vm.$watch('a', cb)
// later, teardown the watcher
unwatch()

$get

现在可以 $get 一个表达式:var value = vm.$get('a + b')

新实例方法

  • vm.$add & vm.$delete

添加/移除数据属性。这两个方法主要是为了规避 ES5 无法侦测添加和移除属性的问题,但更推荐的做法是在初始数据里就定义好所有需要用的属性。

  • vm.$eval:给一个带过滤器的表达式求值:var value = vm.$eval('msg | uppercase')
  • vm.$interpolate:给一个模板字符串求值:var markup = vm.$interpolate('<p>{{msg | uppercase}}</p>')
  • vm.$log:在 console 里输出更容易查看的当前 vm 数据(不会有 getter/setter)。
  • vm.$compile:局部 DOM 编译,并返回一个 decompile 函数。只面对对 Vue 内部实现比较熟悉的高级用户。

可计算属性(Computed Property)改动

$get$set 改为了 getset:

computed: {
  fullName: {
    get: function () {},
    set: function () {}
  }
}

Directive 改动

  • 移除了 v-view,改为 v-component="{{view}}" 的语法
  • v-partial 支持动态 partial:v-partial="{{partialId}}
  • 添加新机制:参数属性(具体例子见下面)
  • v-model
    • 不再支持 contenteditable 元素;请在自定义 directive 里使用第三方的专门库。
    • 添加 lazy 参数属性:<input v-model="msg" lazy> 取代原来的 lazy 实例选项。
    • 添加 number 参数属性:在同步 <input> 值回 model 的时候强制转化类型为 Number
    • 针对 <select> 元素,添加 options 参数属性。具体用法见 这里
  • v-component
    • 添加 v-component="{{view}}" 的动态组件用法
    • 添加 keep-alive 参数属性:在切换动态组件时,保留被替换的组件实例,以便再次切换回去。
    • 添加 wait-for="<eventName>" 参数属性:在切换组件时,等待新组件触发 eventName 事件才执行切换。
    • 添加 transition-mode="in-out | out-in":控制切换组件时的 transition 效果顺序。
      • in-out: 先进后出
      • out-in: 先出后进
  • v-repeat
    • 添加 track-by="<trackID>" 参数属性:两个具有相同 trackID 的数据对象将被视作同一个对象。这个属性的用例是比如从服务器端获得了刷新的数组替换现有数组。新数组可能有大量和现有数据重复的对象,但由于是不同的对象实例,所以 v-repeat 无法复用已有 vm 实例。但如果提供了 track-by,只要这些重复的对象具有相同的 trackID 我们就可以复用已经存在的 vm 实例,避免无谓的重复渲染。
  • v-with
    • 在 0.11 v-with 的用法被明确为 “从父 vm 向子 vm 传递数据”。同时,这个数据的传递现在是单向的。比如在下例中,当 parentKey 改变时,childKey 会跟着改变,但如果在子 vm 里执行 this.childKey = 123,父 vm 的 parentKey 则不受影响。
<div v-component="test" v-with="childKey:parentKey">
  {{childKey}}
</div>
  • 新 directive: v-el。很简单,就是存储一个 DOM 元素的 reference。比如 <div v-el="something"></div> 就变成 this.$$.something

自定义 Directive API 改动

  • 现在 isLiteral: true 的自定义 directive 也可以提供一个 update() 函数。提供之后该 directive 就具有类似 v-component="{{view}}" 这样的特性。
  • 新选项:priority。自定义 Directive 可以通过提供 priority 选项来设定编译优先级,数字越大优先级越高。
  • 新选项:acceptStatement。如果为 true 则会具有类似 v-on="a = a + 1" 这样的特性,即可以在模板里执行一个语句 statement(而不仅仅是表达式 expression)。
  • 移除了 isFnisEmpty 选项。

模板插值改动

  • 现在直接插值一个对象不会自动 JSON.stringify 了,需要用新增的 json 过滤器。
  • 在插值前加一个 * 号来表示一次性插值,不会创建 watcher:{{* oneTime }}

Config API 改动

现在全局的 Vue.config 不再是一个函数而是一个对象。你可以通过直接修改该对象来调整 Vue 的全局设定。

  • config.prefix 现在包含后面的短横。现在的默认值是 "v-"
  • config.delimiters 现在接受完整的开闭标示符:
Vue.config.delimiters = ['(%', '%)']
<h1>(% someText %)</h1>
<div>((% someHTML %))</div>
  • 新选项 config.proto:默认为 true。设置为 false 可以禁止 Vue 改动被观测数组的 __proto__ 属性。
  • 新选项 config.async: 默认为 true。这是为 false 可以禁止 Vue 使用异步 DOM 更新。

Transition API 改动

  • 不再区分 v-transitionv-animation 或是 v-effect;现在全部是 v-transition;
  • 不用再通过 Vue.config 来设置进入/离开的 css class;
  • Vue.effect(id, definition) 变为 Vue.transition(id, definition);作为选项的 effects 也换为 transitions

假定有 v-transition="my-transition",Vue 会进行如下操作:

  1. 在当前 vm 的选项中查找是否有名为 my-transition 的自定义 js transition。如果有,则使用该 transition。
  2. 如果没有对应的 js transition,则自动检查当前元素是否有生效的 css transition 或是 animation 规则;如果有则执行对应的 css transition/animation 效果。进入类为 .my-transition-enter,离开类为 .my-transition-leave。如果只用 v-transition,则默认为 .v-enter.v-leave
  3. 如果没有任何生效的 transition 或 animation CSS 规则,则直接执行 dom 插入/移除操作。

同时,js transition 的 API 也有所改动。下面是一个利用 jQuery 实现 fade 效果的例子:

Vue.transition('fade', {
  beforeEnter: function (el) {
    // 在元素被插入 document 前触发
  },
  enter: function (el, done) {
    // 在元素被插入 document 后触发
    // 动画完成后调用 done 回调
    $(el)
      .css('opacity', 0)
      .animate({ opacity: 1 }, 1000, done)
    // 可选:返回一个 cleanup 函数。如果 transition 在中途
    // 被取消了,会调用这个函数。
    return function () {
      $(el).stop()
    }
  },
  leave: function (el, done) {
    // 和 enter 一样
    $(el)
      .animate({ opacity: 0 }, 1000, done)
    return function () {
      $(el).stop()
    }
  }
})

事件 API 改动

现在可以在 vm 上注册的事件回调中 return false 来取消事件的 bubble (只对 $dispatch$broadcast 有效)

双向过滤器

现在一个过滤器可以同时具备 read 和 write 两个函数,前者在从数据渲染到 DOM 时被调用,后者在从 DOM 同步回 model 是被调用,因此 write 函数只有在和 v-model 搭配使用时才会被调用,并且,在用 v-model 时如果要用过滤器,应该优先使用 write 函数。例子:

Vue.filter('format', {
  read: function (val) {
    return val + '!'
  },
  write: function (val, oldVal) {
    return val.match(/ok/) ? val : oldVal
  }
})

多个元素的 v-ifv-repeat

由于 Vue 的 v-ifv-repeat 必须添加在一个元素上,之前无法实现对一组多个元素的逻辑处理。在 0.11 里,你可以把要处理的多个元素包裹在一个 <template> 抽象容器元素中:

<template v-repeat="item:items">
  <h2>{{item.title}}</h2>
  <p>{{item.subtitle}}</p>
</template>

渲染结果:

<!--v-block-start-->
<h2>title-1</h2>
<p>subtitle-1</p>
<!--v-block-end-->
<!--v-block-start-->
<h2>title-2</h2>
<p>subtitle-2</p>
<!--v-block-end-->

同时,<template> 也可以作为 v-partial 的抽象容器:

<template v-partial="{{partialID}}"></template>

从而实现无包裹元素的动态 partial。

其他

  • vm.$destroy() 现在默认不移除 vm 的 DOM 元素。可以通过传入 vm.$destroy(true) 来移除。
  • 当使用 v-model 是如果在模板里有 inline 的 value 或是 selected 属性,会将其值作为默认值,且会覆盖 js 里的初始值。

改动较多,如果有不清楚的地方请查阅英文文档: http://vuejs.org/

评论
发表评论
5年前

基本每个页面都遇到了问题,主要遇到几个问题

  1. data方式修改,这个有提醒,还好,就是找不到是哪个component
  2. 报错Cannot read property ‘childNodes’ of undefined,在一个filter里面写了一句$(this.el).html(qrnode), 改成this.el.appendChild(qrnode)后好了
  3. $appendTo报错,从vm.$appendTo(this.el);改为 vm.$mount().$appendTo(this.el);
  4. 一个component的ready事件没有执行,这个找不到任何相关资料,这个一直没解决,暂时放弃升级了
5年前

感觉升级好麻烦啊,变动太大,报错还不知道为什么

5年前

@Breeze Feng 没有桌面/移动端的区分,都适用

5年前

Browser Support

Vue.js supports most ECMAScript 5 compliant browsers, essentially IE9+. IE8 and below are not supported.

5年前

请教下lazy是什么语法?

5年前

同问vue.js有移动端版本吗?

5年前
赞了此文章!
5年前

请问vue.js有移动端版本吗?

5年前
赞了此文章!
5年前
赞了此文章!
5年前
赞了此文章!
5年前
赞了此文章!
5年前
赞了此文章!
WRITTEN BY
尤小右
不会搞艺术的程序员不是好设计师
TA的新浪微博
PUBLISHED IN
Vue.js 中文入门

Vue.js 是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API,让编写动态的UI界面变得轻松简单。本专栏是Vue.js的中文版入门教程。

我的收藏