数组与对象更新检测
我们知道,写在data中的数据是实时响应的,如果我们修改了data中的数据,响应DOM的数据也会对应的改变。
但是对于data中的对象(数组也是对象)来说,事情没那么简单。
vue判断一个对象需不需要实时响应看的是这个对象本身,也就是这个对象的引用有无发生改变,那就导致了如果我们往这个对象增删改一个元素,vue是看不到的,也就无法实时更新DOM的数据。
那怎么办?事实上,vue考虑到了这一点。
数组更新检测
对于数组来说,当你在使用数组的下列方法的时候,vue对这些方法进行了改装,使得你操作这个方法改变数组的时候,可以触发vue动态响应:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
我们称这些被改造的方法为变异方法。 变异方法,顾名思义,会改变调用了这些方法的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如 filter()、concat() 和 slice() 。它们不会改变原始数组,而总是返回一个新数组。
当使用非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。然而事实并非如此。
Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。所以不要想太多。
但是我们需要注意的是,如果你采用下面两种方式,Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue- 当你修改数组的长度时,例如:
vm.items.length = newLength
举个栗子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
这一问题使用下面两种方法可以解决:
第一个问题解决方案:
Vue.set(vm.items, indexOfItem, newValue)
// 或者 vm.$set(vm.items, indexOfItem, newValue)
// 或者 vm.items.splice(indexOfItem, 1, newValue)
第二个问题解决方案:
vm.items.splice(newLength)
对象更新检测
由于原理类似,Vue 不能检测对象属性的添加或删除。
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
你可以添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, 'age', 27);
// this.$set(vm.userProfile, 'age', 27)
有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,如果你使用多次Vue.set方法固然可以,但是比较繁琐。
你可以用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:
Object.assign(vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
这样是无效的,你应该这样做:
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
注意:
不管是 Vue.set 还是 Vue.delete,目标对象不能是一个 Vue 实例或 Vue 实例的根数据对象。否则会报以下错误:
[Vue warn]: Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option.