如何从Vue 1.x 迁移到Vue 2.0
FAQ
哇,非常长的一页!是否意味着 Vue 2.0 已经完全不同了呢,是否需要从头学起呢,Vue 1.0 的项目是不是没法迁移了?
非常开心地告诉你,并不是!几乎 90% 的 API 和核心概念都没有变。因为本节包含了很多详尽的阐述以及许多迁移的例子,所以显得有点长。不用担心,你不必从头到尾把本节读一遍!
我该从哪里开始项目迁移呢?
-
首先,在当前项目下运行迁移工具。我们非常谨慎地把高级 Vue 升级过程简化为使用一个简单的命令行工具。当工具识别出旧有的特性后,就会告知你并给出建议,同时附上关于详细信息的链接。
-
然后,浏览本页面的侧边栏列出的内容。如果发现有的标题对你的项目有影响,但是迁移工具没有给出提示,请检查自己的项目。
-
如果你的项目有测试代码,运行并查看仍然失败的地方。如果没有测试代码,在浏览器中打开你的程序,通过导航环顾并留意那些报错或警告信息。
-
现在,你的应用程序应该已彻底完成迁移。如果你渴望了解更多,可以阅读本页面剩余部分 - 或者从介绍部分,从头开始深入新的文档和改进过的指南。由于你已经熟悉一些核心概念,所以许多部分已经被删除掉。
将 Vue 1.x 版本的应用程序迁移到 2.0 要花多长时间?
这取决于几个因素:
-
取决于你应用程序的规模(中小型的基本上一天内就可以搞定)。
-
取决于你分心和开始 2.0 最酷的新功能的次数。 😉 无法判断时间,我们构建 2.0 应用的时候也经常发生这种事!
-
取决于你使用了哪些旧有的特性。大部分可以通过查找和替换(find-and-replace)来实现升级,但有一些可能还是要花点时间。如果你没有遵循最佳实践,Vue 2.0 会尽力强迫你去遵循。这有利于项目的长期运行,但也可能意味着重大重构(尽管有些需要重构的部分可能已经过时)。
如果我升级到到 Vue 2 ,我还必须同时升级 Vuex 和 Vue-Router?
只有 Vue-Router 2 与 Vue 2 保持兼容,所以 Vue-Router 是需要升级的,你必须遵循 Vue-Router 迁移方式来处理。幸运的是, 大多数应用没有很多 router 相关代码,所以迁移可能不会超过一个小时。
对于 Vuex ,版本 0.8+ 与 Vue 2 保持兼容,所以部分不必强制升级。可以促使你立即升级的唯一理由,是你想要使用那些 Vuex 2 中新的高级特性,比如模块(modules)和减少的样板文件(reduced boilerplate)。
模板
片段实例 移除
每个组件必须只有一个根元素。不再允许片段实例,如果你有这样的模板:
<p>foo</p>
<p>bar</p>
最好把整个内容都简单包裹到一个新的元素里,如下所示:
<div>
<p>foo</p>
<p>bar</p>
</div>
升级方式
升级后运行端到端测试套件(end-to-end test suite)或运行应用程序,并查看控制台警告(console warnings)来找出那些模板中有多个根元素的地方。
生命周期钩子函数
beforeCompile
移除
使用 created
钩子函数替代。
升级方式
在代码库中运行迁移工具来找出所有使用此钩子函数的示例。
compiled
替换
使用 mounted
钩子函数替代。
升级方式
在代码库中运行迁移工具来找出所有使用此钩子函数的示例。
attached
移除
使用其他钩子函数内置的 DOM 检测(DOM check)方法。例如,替换如下:
attached: function () {
doSomething()
}
可以这样使用:
mounted: function () {
this.$nextTick(function () {
doSomething()
})
}
升级方式
在代码库中运行迁移工具来找出所有使用此钩子函数的示例。
detached
移除
使用其他钩子函数内的 DOM 检测(DOM check)方法。例如,替换如下:
detached: function () {
doSomething()
}
可以这样使用:
destroyed: function () {
this.$nextTick(function () {
doSomething()
})
}
升级方式
在代码库中运行迁移工具来找出所有使用此钩子函数的示例。
init
重新命名
使用新的 beforeCreate
钩子函数替代,本质上 beforeCreate 和 init 完全相同。init 被重新命名是为了和其他的生命周期方法的命名方式保持一致。
升级方式
在代码库中运行迁移工具来找出所有使用此钩子函数的示例。
ready
替换
使用新的 mounted
钩子函数替代。应该注意的是,使用 mounted
并不能保证钩子函数中的 this.$el 在 document 中。为此还应该引入 Vue.nextTick
/vm.$nextTick
。例如:
mounted: function () {
this.$nextTick(function () {
// 代码保证 this.$el 在 document 中
})
}
升级方式
在代码库中运行迁移工具来找出所有使用此钩子函数的示例。
v-for
v-for
遍历数组时的参数顺序 变更
当包含 index
时,之前遍历数组时的参数顺序是 (index, value)
。现在是 (value, index)
,来和 JavaScript 的原生数组方法(例如 forEach
和 map
)保持一致。
升级方式
在代码库中运行迁移工具来找出那些使用旧有参数顺序的示例。注意,如果你将你的 index 参数命名为一些不通用的名字(例如 position
或 num
),迁移工具将不会把它们标记出来。
v-for
遍历对象时的参数顺序 变更
当包含 key
时,之前遍历对象的参数顺序是 (key, value)
。现在是 (value, key)
,来和常见的对象迭代器(例如 lodash)保持一致。
升级方式
在代码库中运行迁移工具来找出那些使用旧有参数顺序的示例。注意,如果你将你的 key 参数命名为一些不通用的名字(例如 name
或 property
),迁移工具将不会把它们标记出来。
$index
and $key
移除
已经移除了 $index
和 $key
这两个隐式声明变量,以便在 v-for
中显式定义。这可以使没有太多 Vue 开发经验的开发者更好地阅读代码,并且在处理嵌套循环时也能产生更清晰的行为。
升级方式
在代码库中运行迁移工具来找出使用这些移除变量的示例。如果你没有找到,也可以在控制台错误中查找(例如 Uncaught ReferenceError: $index is not defined
)。
track-by
替换
track-by
已经替换为 key
,它的工作方式与其他属性一样,没有 v-bind
或者 :
前缀,它会被作为一个字符串处理。多数情况下,你需要使用具有完整表达式的动态绑定(dynamic binding)来替换静态的 key。例如,替换:
<div v-for="item in items" track-by="id">
你现在应该写为:
<div v-for="item in items" v-bind:key="item.id">
升级方式
在代码库中运行迁移工具来找出那些使用track-by
的示例。
v-for
范围值 变更
之前,v-for="number in 10"
的 number
从 0 开始到 9 结束,现在从 1 开始,到 10 结束。
升级方式
在代码库中使用正则 /\w+ in \d+/
搜索。当出现在 v-for
中,请检查是否受到影响。
Props
coerce
Prop的参数 移除
如果需要检查 prop 的值,创建一个内部的 computed 值,而不再在 props 内部去定义,例如:
props: {
username: {
type: String,
coerce: function (value) {
return value
.toLowerCase()
.replace(/\s+/, '-')
}
}
}
现在应该写为:
props: {
username: String,
},
computed: {
normalizedUsername: function () {
return this.username
.toLowerCase()
.replace(/\s+/, '-')
}
}
这样有一些好处:
- 你可以对保持原始 prop 值的操作权限。
- 通过给予验证后的值一个不同的命名,强制开发者使用显式申明。
升级方式
运行迁移工具找出包含 coerce
选项的实例。
twoWay
Prop 的参数 移除
Props 现在只能单项传递。为了对父组件产生反向影响,子组件需要显式地传递一个事件而不是依赖于隐式地双向绑定。详见:
- 自定义组件事件
- 自定义输入组件 (使用组件事件)
- 全局状态管理
升级方式
运行 迁移工具,找出含有 twoWay
参数的实例。
v-bind
的 .once
和.sync
修饰符 移除
Props 现在只能单向传递。为了对父组件产生反向影响,子组件需要显式地传递一个事件而不是依赖于隐式地双向绑定。详见:
- 自定义组件事件
- 自定义输入组件 (使用组件事件)
- 全局状态管理
升级方式
运行迁移工具找到使用 .once
和 .sync
修饰符的实例。
修改 Props 弃用
组件内修改 prop 是反模式(不推荐的)的。比如,先声明一个 prop ,然后在组件中通过 this.myProp = 'someOtherValue'
改变 prop 的值。根据渲染机制,当父组件重新渲染时,子组件的内部 prop 值也将被覆盖。
大多数情况下,改变 prop 值可以用以下选项替代:
- 通过 data 属性,用prop去设置一个data属性的默认值。
- 通过 computed 属性。
升级方式
运行端对端测试,查看关于 prop 修改的控制台警告信息。
根实例的 Props 替换
对于一个根实例来说 (比如:用 new Vue({ ... })
创建的实例),只能用 propsData
而不是 props
。
升级方式
运行端对端测试,将会弹出 failed tests 来通知你使用 props
的根实例已经失效。
Built-In 指令
v-bind
真/假值 变更
在2.0中使用 v-bind
时,只有 null
, undefined
, 和 false
被看作是假。这意味着,0
和空字符串将被作为真值渲染。比如 v-bind:draggable="''"
将被渲染为 draggable="true"
。
对于枚举属性,除了以上假值之外,字符串 "false"
也会被渲染为 attr="false"
。
注意,对于其它钩子函数 (如 `v-if` 和 `v-show`), 他们依然遵循 js 对真假值判断的一般规则。
升级方式
运行端到端测试,如果你app的任何部分有可能被这个升级影响到,将会弹出failed tests
用 v-on
监听原生事件 变更
现在在组件上使用 v-on
只会监听自定义事件(组件用 $emit
触发的事件)。如果要监听根元素的原生事件,可以使用 .native
修饰符,比如:
<my-component v-on:click.native="doSomething"></my-component>
升级方式
运行端对端测试,如果你app的任何部分有可能被这个升级影响到,将会弹出failed tests
带有 debounce
的 v-model
移除
Debouncing 曾经被用来控制 Ajax 请求及其它高耗任务的频率。 Vue 中v-model
的 debounce
属性参数使得在一些简单情况下非常容易实现这种控制。但实际上,这是控制了 状态更新 的频率,而不是控制高耗时任务本身。这是个微小的差别,但是会随着应用增长而显现出局限性。
例如在设计一个搜索提示时的局限性:
使用 debounce
参数,便无法观察 "Typing" 的状态。因为无法对输入状态进行实时检测。然而,通过将 debounce
与 Vue 解耦,可以仅仅只延迟我们想要控制的操作,从而避开这些局限性:
<!--
通过使用lodash或者其它库的debounce函数,
我们相信 debounce 实现是一流的,
并且可以随处使用它,不仅仅是在模板中。
-->
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.js"></script>
<div id="debounce-search-demo">
<input v-model="searchQuery" placeholder="Type something">
<strong>{{ searchIndicator }}</strong>
</div>
new Vue({
el: '#debounce-search-demo',
data: {
searchQuery: '',
searchQueryIsDirty: false,
isCalculating: false
},
computed: {
searchIndicator: function () {
if (this.isCalculating) {
return '⟳ Fetching new results'
} else if (this.searchQueryIsDirty) {
return '... Typing'
} else {
return '✓ Done'
}
}
},
watch: {
searchQuery: function () {
this.searchQueryIsDirty = true
this.expensiveOperation()
}
},
methods: {
// 这是 debounce 实现的地方。
expensiveOperation: _.debounce(function () {
this.isCalculating = true
setTimeout(function () {
this.isCalculating = false
this.searchQueryIsDirty = false
}.bind(this), 1000)
}, 500)
}
})
这种方式的另外一个优点是:当包裹函数执行时间与延时时间相当时,将会等待较长时间。比如,当给出搜索建议时,要等待用户输入停止一段时间后才给出建议,这个体验非常差。其实,这时候更适合用 throttling 函数。因为现在你可以自由的使用类似 lodash 之类的库,所以很快就可以用 throttling 重构项目。
Upgrade Path
运行迁移工具找出使用 debounce
参数的 实例。
使用 lazy
或者 number
参数的 v-model
。 替换
lazy
和 number
参数现在以修饰符的形式使用,这样看起来更加清晰,而不是这样:
<input v-model="name" lazy>
<input v-model="age" type="number" number>
现在写成这样:
<input v-model.lazy="name">
<input v-model.number="age" type="number">
升级方式
运行 迁移工具找到这些弃用参数。
使用内联 value
的v-model
移除
v-model
不再以内联 value
方式初始化的初值了,显然他将以实例的 data 相应的属性作为真正的初值。
这意味着以下元素:
<input v-model="text" value="foo">
在data选项中有下面写法的:
data: {
text: 'bar'
}
将渲染 model 为 'bar' 而不是 'foo' 。同样,对 <textarea>
已有的值来说:
<textarea v-model="text">
hello world
</textarea>
必须保证 text
初值为 "hello world"
升级方式
升级后运行端对端测试,注意关于v-model
内联参数的 console warnings
v-model
with v-for
Iterated Primitive Values 移除
像这样的写法将失效:
<input v-for="str in strings" v-model="str">
因为 <input>
将被编译成类似下面的 js 代码:
strings.map(function (str) {
return createElement('input', ...)
})
这样,v-model
的双向绑定在这里就失效了。把 str
赋值给迭代器里的另一个值也没有用,因为它仅仅是函数内部的一个变量。
替代方案是,你可以使用对象数组,这样v-model
就可以同步更新对象里面的字段了,例如:
<input v-for="obj in objects" v-model="obj.str">
升级方式
运行测试,如果你的 app 有地方被这个更新影响到的话将会弹出failed tests提示。
带有 !important
的v-bind:style
移除
这样写将失效:
<p v-bind:style="{ color: myColor + ' !important' }">hello</p>
如果确实需要覆盖其它的 !important
,最好用字符串形式去写:
<p v-bind:style="'color: ' + myColor + ' !important'">hello</p>
升级方式
运行 迁移帮助工具。找到含有 !important
的style绑定对象。
v-el
和v-ref
替换
简单起见, v-el
和 v-ref
合并为一个 ref
属性了,可以在组件实例中通过 $refs
来调用。这意味着 v-el:my-element
将写成这样: ref="myElement"
, v-ref:my-component
变成了这样: ref="myComponent"
。绑定在一般元素上时,ref
指DOM元素,绑定在组件上时,ref
为一组件实例。
因为 v-ref
不再是一个指令了而是一个特殊的属性,它也可以被动态定义了。这样在和v-for
结合的时候是很有用的:
<p v-for="item in items" v-bind:ref="'item' + item.id"></p>
以前 v-el
/v-ref
和 v-for
一起使用将产生一个DOM数组或者组件数组,因为没法给每个元素一个特定名字。现在你还仍然可以这样做,给每个元素一个同样的ref
:
<p v-for="item in items" ref="items"></p>
和 1.x 中不同, $refs
不是响应的,因为它们在渲染过程中注册/更新。只有监听变化并重复渲染才能使它们响应。
另一方面,设计$refs
主要是提供给 js 程序访问的,并不建议在模板中过度依赖使用它。因为这意味着在实例之外去访问实例状态,违背了 Vue 数据驱动的思想。
升级方式
运行迁移工具找出实例中的 v-el
和 v-ref
。
v-show
后面使用v-else
移除
v-else
不能再跟在 v-show
后面使用。请在v-if
的否定分支中使用v-show
来替代。例如:
<p v-if="foo">Foo</p>
<p v-else v-show="bar">Not foo, but bar</p>
现在应该写出这样:
<p v-if="foo">Foo</p>
<p v-if="!foo && bar">Not foo, but bar</p>
升级方式
运行迁移工具找出实例中存在的 v-else
以及 v-show
。
自定义指令 简化
在新版中,指令的使用范围已经大大减小了:现在指令仅仅被用于低级的 DOM 操作。大多数情况下,最好是把模板作为代码复用的抽象层。
显要的改变有如下几点:
- 指令不再拥有实例。意思是,在指令的钩子函数中不再拥有实例的
this
。替代的是,你可以在参数中接受你需要的任何数据。如果确实需要,可以通过el
来访问实例。 - 类似
acceptStatement
,deep
,priority
等都已被弃用。为了替换双向
指令,见 示例。 - 现在有些钩子的意义和以前不一样了,并且多了两个钩子函数。
幸运的是,新钩子更加简单,更加容易掌握。详见 自定义指令指南。
升级方式
运行迁移工具找到定义指令的地方。在 helper 工具会把这些地方标记出来,因为很有可能这些地方需要重构。
指令 .literal
修饰符 移除
.literal
修饰符已经被移除,为了获取一样的功能,可以简单地提供字符串修饰符作为值。
示例,如下更改:
<p v-my-directive.literal="foo bar baz"></p>
只是:
<p v-my-directive="'foo bar baz'"></p>
升级方式
运行迁移工具找到实例中使用 `.literal` 修饰符的地方。
过渡
transition
参数 替换
Vue 的过渡系统有了彻底的改变,现在通过使用 <transition>
和 <transition-group>
来包裹元素实现过渡效果,而不再使用 transition
属性。详见 Transitions guide。
升级方式
运行迁移工具找到使用 transition
属性的地方。
可复用的过渡 Vue.transition
替换
在新的过渡系统中,可以通过模板复用过渡效果。
升级方式
运行迁移工具找到使用 transition
属性的地方。
过渡的 stagger
参数 移除
如果希望在列表渲染中使用渐近过渡,可以通过设置元素的 data-index
(或类似属性)来控制时间。 请参考这个例子。
升级方式
运行迁移工具找到使用 transition
属性的地方。升级期间,你可以“过渡”到新的过渡策略。
事件
events
选项 移除
events
选项被弃用。事件处理器现在在 created
钩子中被注册。参考详细示例 $dispatch
and $broadcast
迁移指南
Vue.directive('on').keyCodes
替换
新的简明配置 keyCodes
的方式是通过 Vue.config.keyCodes
例如:
// v-on:keyup.f1 不可用
Vue.config.keyCodes.f1 = 112
升级方式
运行迁移工具找到过时的 keyCode
配置
$dispatch
和 $broadcast
替换
$dispatch
和 $broadcast
已经被弃用。请使用更多简明清晰的组件间通信和更好的状态管理方案,如:Vuex.
因为基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会变得越来越脆弱。这种事件方式确实不太好,我们也不希望在以后让开发者们太痛苦。并且$dispatch
和 $broadcast
也没有解决兄弟组件间的通信问题。
对于$dispatch
和 $broadcast
最简单的升级方式就是:通过使用事件中心,允许组件自由交流,无论组件处于组件树的哪一层。由于 Vue 实例实现了一个事件分发接口,你可以通过实例化一个空的 Vue 实例来实现这个目的。
这些方法的最常见用途之一是父子组件的相互通信。在这些情况下,你可以使用 v-on
监听子组件上 $emit 的变化。这可以允许你很方便的添加事件显性。
然而,如果是跨多层父子组件通信的话, $emit
并没有什么用。相反,用集中式的事件中间件可以做到简单的升级。这会让组件之间的通信非常顺利,即使是兄弟组件。因为 Vue 通过事件发射器接口执行实例,实际上你可以使用一个空的 Vue 实例。
比如,假设我们有个 todo 的应用结构如下:
Todos
|-- NewTodoInput
|-- Todo
|-- DeleteTodoButton
可以通过单独的事件中心管理组件间的通信:
// 将在各处使用该事件中心
// 组件通过它来通信
var eventHub = new Vue()
然后在组件中,可以使用 $emit
, $on
, $off
分别来分发、监听、取消监听事件:
// NewTodoInput
// ...
methods: {
addTodo: function () {
eventHub.$emit('add-todo', { text: this.newTodoText })
this.newTodoText = ''
}
}
// DeleteTodoButton
// ...
methods: {
deleteTodo: function (id) {
eventHub.$emit('delete-todo', id)
}
}
// Todos
// ...
created: function () {
eventHub.$on('add-todo', this.addTodo)
eventHub.$on('delete-todo', this.deleteTodo)
},
// 最好在组件销毁前
// 清除事件监听
beforeDestroy: function () {
eventHub.$off('add-todo', this.addTodo)
eventHub.$off('delete-todo', this.deleteTodo)
},
methods: {
addTodo: function (newTodo) {
this.todos.push(newTodo)
},
deleteTodo: function (todoId) {
this.todos = this.todos.filter(function (todo) {
return todo.id !== todoId
})
}
}
在简单的情况下这样做可以替代 $dispatch
和 $broadcast
,但是对于大多数复杂情况,更推荐使用一个专用的状态管理层如:Vuex 。
升级方式
运行迁移工具找出使用 $dispatch
和 $broadcast
的实例。
过滤器
插入文本之外的过滤器 移除
现在过滤器只能用在插入文本中 ({% raw %}{{ }}{% endraw %}
tags)。我们发现在指令(如:v-model
, v-on
等)中使用过滤器使事情变得更复杂。像v-for
这样的列表过滤器最好把处理逻辑作为一个计算属性放在js里面,这样就可以在整个模板中复用。
总之,能在原生js中实现的东西,我们尽量避免引入一个新的符号去重复处理同样的问题。下面是如何替换 Vue 内置过滤器:
替换 debounce
过滤器
不再这样写
<input v-on:keyup="doStuff | debounce 500">
methods: {
doStuff: function () {
// ...
}
}
请使用 lodash's debounce
(也有可能是 throttle
) 来直接控制高耗任务。可以这样来实现上面的功能:
<input v-on:keyup="doStuff">
methods: {
doStuff: _.debounce(function () {
// ...
}, 500)
}
这种写法的更多优点详见: v-model
示例.
替换 limitBy
过滤器
不再这样写:
<p v-for="item in items | limitBy 10">{{ item }}</p>
在 computed 属性中使用js内置方法: .slice
method:
<p v-for="item in filteredItems">{{ item }}</p>
computed: {
filteredItems: function () {
return this.items.slice(0, 10)
}
}
替换 filterBy
过滤器
不再这样写:
<p v-for="user in users | filterBy searchQuery in 'name'">{{ user.name }}</p>
在 computed 属性中使用js内置方法 .filter
method:
<p v-for="user in filteredUsers">{{ user.name }}</p>
computed: {
filteredUsers: function () {
var self = this
return self.users.filter(function (user) {
return user.name.indexOf(self.searchQuery) !== -1
})
}
}
js原生的 .filter
同样能实现很多复杂的过滤器操作,因为可以在计算 computed 属性中使用所有js方法。比如,想要通过匹配用户名字和电子邮箱地址(不区分大小写)找到用户:
var self = this
self.users.filter(function (user) {
var searchRegex = new RegExp(self.searchQuery, 'i')
return user.isActive && (
searchRegex.test(user.name) ||
searchRegex.test(user.email)
)
})
替换 orderBy
过滤器
不这样写:
<p v-for="user in users | orderBy 'name'">{{ user.name }}</p>
而是在 computed 属性中使用 lodash's orderBy
(或者可能是 sortBy
):
<p v-for="user in orderedUsers">{{ user.name }}</p>
computed: {
orderedUsers: function () {
return _.orderBy(this.users, 'name')
}
}
甚至可以字段排序:
_.orderBy(this.users, ['name', 'last_login'], ['asc', 'desc'])
升级方式
运行迁移工具找到指令中使用的过滤器。如果有些没找到,看看控制台错误信息。
过滤器参数符号 变更
现在过滤器参数形式可以更好地与js函数调用方式一致,因此不用再用空格分隔参数:
<p>{{ date | formatDate 'YY-MM-DD' timeZone }}</p>
现在用圆括号括起来并用逗号分隔:
<p>{{ date | formatDate('YY-MM-DD', timeZone) }}</p>
升级方式
运行迁移工具找到老式的调用符号,如果有遗漏,请看控制台错误信息。
内置文本过滤器 移除
尽管插入文本内部的过滤器依然有效,但是所有内置过滤器已经移除了。取代的是,推荐在每个区域使用更专业的库来解决。(比如用 date-fns
来格式化日期,用 accounting
来格式化货币)。
对于每个内置过滤器,我们大概总结了下该怎么替换。代码示例可能写在自定义 helper 函数,方法或计算属性中。
替换 json
过滤器
不用一个个改,因为Vue已经帮你自动格式化好了,无论是字符串,数字还是数组,对象。如果想用js的 JSON.stringify
功能去实现,你也可以把它写在方法或者计算属性里面。
替换 capitalize
过滤器
text[0].toUpperCase() + text.slice(1)
替换 uppercase
过滤器
text.toUpperCase()
替换 lowercase
过滤器
text.toLowerCase()
替换 pluralize
过滤器
NPM 上的 pluralize 库可以很好的实现这个功能。如果仅仅想将特定的词格式化成复数形式或者想给特定的值('0')指定特定的输出,也可以很容易地自定义复数格式化过滤器:
function pluralizeKnife (count) {
if (count === 0) {
return 'no knives'
} else if (count === 1) {
return '1 knife'
} else {
return count + 'knives'
}
}
Replacing the currency
Filter
对于简单的问题,可以这样做:
'$' + price.toFixed(2)
大多数情况下,仍然会有奇怪的现象(比如 0.035.toFixed(2)
向上取舍得到 0.04
,但是 0.045
向下取舍却也得到 0.04
)。解决这些问题可以使用 accounting
库来实现更多可靠的货币格式化。
升级方式
运行迁移工具找到舍弃的过滤器。如果有些遗漏,请参考控制台错误信息。
Two-Way Filters replaced
Some users have enjoyed using two-way filters with v-model
to create interesting inputs with very little code. While seemingly simple however, two-way filters can also hide a great deal of complexity - and even encourage poor UX by delaying state updates. Instead, components wrapping an input are recommended as a more explicit and feature-rich way of creating custom inputs.
As an example, we'll now walk the migration of a two-way currency filter:
It mostly works well, but the delayed state updates can cause strange behavior. For example, click on the Result
tab and try entering 9.999
into one of those inputs. When the input loses focus, its value will update to $10.00
. When looking at the calculated total however, you'll see that 9.999
is what's stored in our data. The version of reality that the user sees is out of sync!
To start transitioning towards a more robust solution using Vue 2.0, let's first wrap this filter in a new <currency-input>
component:
This allows us add behavior that a filter alone couldn't encapsulate, such as selecting the content of an input on focus. Now the next step will be to extract the business logic from the filter. Below, we pull everything out into an external currencyValidator
object:
This increased modularity not only makes it easier to migrate to Vue 2, but also allows currency parsing and formatting to be:
- unit tested in isolation from your Vue code
- used by other parts of your application, such as to validate the payload to an API endpoint
Having this validator extracted out, we've also more comfortably built it up into a more robust solution. The state quirks have been eliminated and it's actually impossible for users to enter anything wrong, similar to what the browser's native number input tries to do.
We're still limited however, by filters and by Vue 1.0 in general, so let's complete the upgrade to Vue 2.0:
You may notice that:
- Every aspect of our input is more explicit, using lifecycle hooks and DOM events in place of the hidden behavior of two-way filters.
- We can now use
v-model
directly on our custom inputs, which is not only more consistent with normal inputs, but also means our component is Vuex-friendly. - Since we're no longer using filter options that require a value to be returned, our currency work could actually be done asynchronously. That means if we had a lot of apps that had to work with currencies, we could easily refactor this logic into a shared microservice.
Upgrade Path
Run the迁移工具on your codebase to find examples of filters used in directives like v-model
. If you miss any, you should also see console errors.
Slots
重名的 Slots 移除
同一模板中的重名 <slot>
已经弃用。当一个 slot 已经被渲染过了,那么就不能在同一模板其它地方被再次渲染了。如果要在不同位置渲染同一内容,可一用prop来传递。
升级方式
更新后运行测试,查看 控制台警告信息 关于重名slots的提示 v-model
。
slot
样式参数 移除
通过具名 <slot>
插入的片段不再保持 slot
的参数。请用一个包裹元素来控制样式。或者用更高级方法:通过编程方式修改内容 :render functions 。
升级方式
运行迁移工具找到选择slots标签CSS选择器(例如: [slot="my-slot-name"]
) 。
特殊属性
keep-alive
属性 替换
keep-alive
不再是一个特殊属性而是一个包裹组件,类似于 <transition>
比如:
<keep-alive>
<component v-bind:is="view"></component>
</keep-alive>
这样可以在含多种状态子组件中使用 <keep-alive>
:
<keep-alive>
<todo-list v-if="todos.length > 0"></todo-list>
<no-todos-gif v-else></no-todos-gif>
</keep-alive>
当 `
和 <transition>
一起使用时,确保把内容包裹在内:
<transition>
<keep-alive>
<component v-bind:is="view"></component>
</keep-alive>
</transition>
升级方式
运行迁移工具找到keep-alive
属性。
计算插值todaymark
属性内部的计算插值 移除
属性内部的计算插值已经不能再使用了:
<button class="btn btn-{{ size }}"></button>
应该写成行内表达式:
<button v-bind:class="'btn btn-' + size"></button>
或者计算属性:
<button v-bind:class="buttonClasses"></button>
computed: {
buttonClasses: function () {
return 'btn btn-' + size
}
}
升级方式
运行迁移工具找到属性内部的计算插值
HTML 计算插值 移除
HTML 的计算插值 ({% raw %}{{{ foo }}}{% endraw %}
) 已经移除,取代的是 v-html
指令.
升级方式
运行迁移工具找到 HTML 计算插值。
单次绑定替换
单次绑定 ({% raw %}{{* foo }}{% endraw %}
) 已经被新的 v-once
directive 取代。
升级方式
运行 迁移工具找到单次绑定使用位置。
响应
vm.$watch
changed
通过 vm.$watch
创建的观察器现在将在组件渲染时被激活。这样可以让你在组件渲染前更新状态,不用做不必要的更新。比如可以通过观察组件的prop变化来更新组件本身的值。
如果以前通过 vm.$watch
在组件更新后与 DOM 交互,现在就可以通过updated
生命周期钩子来做这些。
升级方式
运行测试,如果有依赖于老方法的观察器将弹出 failed tests。
vm.$set
变更
vm.$set
只是 Vue.set
的别名。
升级方式
运行迁移工具找到过时的用法
vm.$delete
变更
vm.$delete
现在只是: Vue.delete
别名。
升级方式
运行迁移工具找到过时的用法
Array.prototype.$set
弃用
用 Vue.set
替代
升级方式
运行迁移工具找到数组上的.$set
。如有遗漏请参考控制台错误信息。
Array.prototype.$remove
移除
用 Array.prototype.splice
替代,例如:
methods: {
removeTodo: function (todo) {
var index = this.todos.indexOf(todo)
this.todos.splice(index, 1)
}
}
或者更好的方法,直接给除去的方法一个index参数:
methods: {
removeTodo: function (index) {
this.todos.splice(index, 1)
}
}
升级方式
运行迁移工具找到数组上的.$remove
。如有遗漏请参考控制台错误信息
Vue实例上的Vue.set
和 Vue.delete
移除
Vue.set
和 Vue.delete
在实例上将不再起作用。现在都强制在实例的data选项中声明所有顶级响应值。如果删除实例属性或实例$data
上的某个值,直接将它设置为null即可。
升级方式
运行迁移工具找到实例中的 Vue.set
或 Vue.delete
。如有遗漏请参考控制台错误信息。
替换 vm.$data
移除
现在禁止替换实例的 $data。这样防止了响应系统的一些极端情况并且让组件状态更加可控可预测(特别是对于存在类型检查的系统)。
升级方式
运行迁移工具找到覆盖 vm.$data
的位置。如有遗漏请参考控制台警告信息。
vm.$get
移除
可以直接取回响应数据。
升级方式
运行 迁移工具找到 vm.$get
的位置。如有遗漏请参考 控制台错误信息。
围绕 DOM 的实例方法
vm.$appendTo
移除
使用 DOM 原生方法:
myElement.appendChild(vm.$el)
升级方式
运行迁移工具找到 vm.$appendTo
的位置。如果有遗漏可以参考控制台错误信息。
vm.$before
移除
使用 DOM 原生方法:
myElement.parentNode.insertBefore(vm.$el, myElement)
升级方式
运行 迁移工具找到 vm.$before
。如有遗漏,请参考 控制台错误信息。
vm.$after
移除
使用 DOM 原生方法:
myElement.parentNode.insertBefore(vm.$el, myElement.nextSibling)
如果 myElement
是最后一个节点也可以这样写:
myElement.parentNode.appendChild(vm.$el)
升级方式
运行迁移工具找到 vm.$after
的位置。如有遗漏,请参考控制台错误信息。
vm.$remove
移除
使用 DOM 原生方法:
vm.$el.remove()
升级方式
运行 迁移工具找到vm.$remove
。如有遗漏,请参考控制台错误信息。
底层实例方法
vm.$eval
移除
尽量不要使用,如果必须使用该功能并且不肯定如何使用请参考 the forum。
升级方式
运行迁移工具找到使用 vm.$eval
的位置。如有遗漏请参考控制台错误信息。
vm.$interpolate
移除
尽量不要使用,如果必须使用该功能并且不肯定如何使用请参考 the forum。
升级方式
运行迁移工具找到vm.$interpolate
。如有遗漏请参考控制台错误信息.
vm.$log
移除
请使用 Vue Devtools 感受最佳debug体验。
升级方式
运行迁移工具找到 vm.$log
。如有遗漏请参考控制台错误信息.
实例 DOM 选项
replace: false
移除
现在组件总是会替换掉他们被绑定的元素。为了模仿replace: false
的行为,可以用一个和将要替换元素类似的元素将根组件包裹起来:
new Vue({
el: '#app',
template: '<div> ... </div>'
})
或者使用渲染函数:
new Vue({
el: '#app',
render: function (h) {
h('div', {
attrs: {
id: 'app',
}
}, /* ... */)
}
})
升级方式
运行迁移工具找到 replace: false
使用的位置。
全局配置
Vue.config.debug
移除
不再需要,因为警告信息将默认在堆栈信息里输出。
升级方式
运行迁移工具找到包含Vue.config.debug
的地方。
Vue.config.async
移除
异步操作现在需要渲染性能的支持。
升级方式
运行 迁移工具找到使用Vue.config.async
的实例。
Vue.config.delimiters
替换
以 模板选项的方式使用。这样可以在使用自定义分隔符时避免影响第三方模板。
升级方式
运行迁移工具找到使用Vue.config.delimiters
的实例。
Vue.config.unsafeDelimiters
移除
HTML 插入 替换为 v-html
.
升级方式
运行迁移工具找到 Vue.config.unsafeDelimiters
。然后 helper 工具也会找到HTML插入的实例,可以用`v-html`来替换。
全局 API
带 el
的 Vue.extend
移除
el 选项不再在 Vue.extend
中使用。仅在实例创建参数中可用。
升级方式
更新后运行测试在控制台警告信息中找到关于带有Vue.extend
的el
。
Vue.elementDirective
移除
用组件来替代
升级方式
运行迁移工具找到包含Vue.elementDirective
的实例。
Vue.partial
移除
Partials 已被移除,取而代之的是更明确的组件之间的数据流--props。除非你正在使用一个部分性能关键型区域,否则建议只使用一个normal component来代替。如果你是动态绑定部分的name
,您可以使用dynamic component。
如果你碰巧在你的应用程序的性能关键部分使用partials
,那么你应该升级到functional components。它们必须在纯JS / JSX文件中(而不是在.vue
文件中),并且是无状态的和无实例的,就像partials
。这使得渲染极快。
functional components
相对于partials
一个好处是它们可以更具动态性,因为它们允许您访问JavaScript的全部功能。然而,这是有成本的。如果你从来没有使用过渲染式的组件框架,你可能需要花费更长的时间来学习它们。
升级方式
运行迁移工具找到包含 Vue.partial
的实例
原文: http:/ /vuejs.org/guide/migration.html