List Rendering - Array and Object Change Detection
# List Rendering - Array and Object Change Detection
Array Change Detection API (opens new window) Object Change Detection API (opens new window)
# Array Change Detection
# Mutation Methods
Vue wraps the observed array's mutation methods so they will also trigger view updates. The wrapped methods include:
push()add to endpop()remove from endshift()remove from beginningunshift()add to beginningsplice()splicesort()sortreverse()reverse
Array Instance Methods (opens new window)
# Replacing an Array
Mutation methods, as the name suggests, mutate the original array they are called on. In comparison, there are also non-mutating methods, e.g. filter(), concat() and slice(). They do not mutate the original array but always return a new array. When working with non-mutating methods, you can replace the old array with the new one:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
2
3
You might think this will cause Vue to throw away the existing DOM and re-render the entire list. Luckily, that is not the case. Vue implements some smart heuristics to maximize DOM element reuse, so replacing an array with another array containing overlapping elements is a very efficient operation.
# Caveats
Due to limitations in JavaScript, Vue cannot detect the following changes to an array:
- When you directly set an item by index, e.g.:
vm.items[indexOfItem] = newValue - When you modify the length of the array, e.g.:
vm.items.length = newLength
To solve the first type of problem, the following two approaches achieve the same effect as vm.items[indexOfItem] = newValue, while also triggering state updates in the reactivity system:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
2
3
4
You can also use the vm.$set (opens new window) instance method, which is an alias for the global Vue.set:
vm.$set(vm.items, indexOfItem, newValue)
To solve the second type of problem, you can use splice:
vm.items.splice(newLength)
# Object Change Detection Caveats
Example of iterating over an object in a list
<div v-for="(item, key, index) in obj"> </div>1
2
Again due to limitations of JavaScript, Vue cannot detect property addition or deletion on objects:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` is now reactive
vm.b = 2
// `vm.b` is NOT reactive
2
3
4
5
6
7
8
9
For already created instances, Vue does not allow dynamically adding root-level reactive properties.
However, you can add reactive properties to a nested object using the Vue.set(object, propertyName, value) method. For example, given:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
2
3
4
5
6
7
You can add a new age property to the nested userProfile object:
Vue.set(vm.userProfile, 'age', 27)
You can also use the vm.$set instance method, which is just an alias for the global Vue.set:
vm.$set(vm.userProfile, 'age', 27)
Sometimes you may need to assign a number of new properties to an existing object, for example using Object.assign() or _.extend(). In such cases, you should create a fresh object with properties from both objects. So instead of doing this:
Object.assign(vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
2
3
4
You should do this:
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
2
3
4
# Summary
To make array updates reactive, you can use:
Mutation methods (push, pop, unshift, shift, splice, sort, reverse)
Replace the array reference (for methods that don't mutate the original array)
Use the Vue.set() method
To make object property additions or deletions reactive, you can use:
Replace the object reference
Use the Vue.set() method
Vue.set() syntax:
// Update data in an array
Vue.set(vm.items, indexOfItem, newValue)
i.e. Vue.set(originalArray, index, newData)
// Update data in an object
Vue.set(object, propertyName, value)
i.e. Vue.set(originalObject, propertyName, value)
2
3
4
5
6
7
The vm.$set() instance method is an alias for the global Vue.set() method