Pass in object or array for reactivity/detect data changes
The following examples pass in 3 kind of variable as data:
- user (object with property)
- count (integer)
- numbers (array)
let user = { id: 0}let count = 0let numbers = [0]const vm = new Vue({ data: { user: user, count: count, numbers: numbers }, computed: { userId() { return this.user.id } }})
To maintain reactivity/change detection between Vue and external source, you should only pass in object (modify properties) or array (modify array).
user.id = 100console.log(vm.user.id) // Output: 100console.log(vm.userId) // Output: 100numbers.push(99)console.log(vm.numbers) // Output: [0, 99]numbers[0] = 88console.log(vm.numbers) // Output: [88, 99]
Note: It is recommended to modify array using Vue.set(numbers, 0, 88)
rather than numbers[0] = 88
in order for it to work in v-for
list.
If you modify the variable directly, the changes can't be deteced.
count = 1console.log(vm.count) // Output: 0 (Not Updated)user = { id: 1 }console.log(vm.user.id) // Output: 100 (Not Updated)console.log(vm.userId) // Output: 100 (Not Updated)numbers = [33, 44]console.log(vm.numbers) // Output: [88, 99] (Not Updated)
Use Vue.set to add dynamic property to object
JavaScript cannot detect addition or deletion of property, so it's preferable to pre-declare all data properties.
const user = { id: 0}const vm = new Vue({ data: { user: user }, computed: { userId() { return this.user.id }, userAge() { return this.user.age }, userWeight() { return this.user.weight } }})
Observations:
user.id
work as expected because it is a pre-declared propertyuser.weight
work as expected because we useVue.set
to add property.user.age
had unexpected result forcomputed userAge
because.age
property is added on runtime directly (not viaVue.set
). If you already added a runtime property directlty, subsequent call toVue.set
has no effect to restore reactivity.
user.id = 100console.log(user.id) // Output: 100console.log(vm.user.id) // Output: 100console.log(vm.userId) // Output: 100vm.user.id = 99console.log(user.id) // Output: 99console.log(vm.user.id) // Output: 99console.log(vm.userId) // Output: 99Vue.set(user, 'weight', 60) // use this to add new propertyconsole.log(user.weight) // Output: 60console.log(vm.user.weight) // Output: 60console.log(vm.userWeight) // Output: 60user.weight = 59console.log(user.weight) // Output: 59console.log(vm.user.weight) // Output: 59console.log(vm.userWeight) // Output: 59user.age = 70console.log(user.age) // Output: 70console.log(vm.user.age) // Output: 70console.log(vm.userAge) // Output: 70user.age = 69console.log(user.age) // Output: 69console.log(vm.user.age) // Output: 69console.log(vm.userAge) // Output: 70 (Not Updated)Vue.set(user, 'age', 68) // too late as age property is already added directlyconsole.log(user.age) // Output: 68console.log(vm.user.age) // Output: 68console.log(vm.userAge) // Output: 70 (Not Updated)
Copy multiple properties to object
Vue.js recommend the following:
Sometimes you may want to assign a number of properties to an existing object, for example using Object.assign() or _.extend(). However, new properties added to the object will not trigger changes. In such cases, create a fresh object with properties from both the original object and the mixin object:
// instead of `Object.assign(this.someObject, { a: 1, b: 2 })`this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
If Object.assign
applied on vm.user
(not original user
) to create fresh object (using {}
), and the original user
object outside of Vue is not updated (since we create a fresh object within Vue).
const user = { id: 0}const vm = new Vue({ data: { user: user }, computed: { userId() { return this.user.id }, userName() { return this.user.name } },})vm.user = Object.assign({}, vm.user, { id: 7, name: 'Desmond Lua', height: 186 })console.log(user.id) // Output: 0 (Not Updated)console.log(vm.user.id) // Output: 7console.log(vm.userId) // Output: 7console.log(user.name) // Output: undefined (Not Updated)console.log(vm.user.name) // Output: Desmond Luaconsole.log(vm.userName) // Output: Desmond Luavm.user.name = 'Sam Lee'console.log(user.name) // Output: undefined (Not Updated)console.log(vm.user.name) // Output: Sam Leeconsole.log(vm.userName) // Output: Sam Leeuser.name = 'Albert Hitch'console.log(user.name) // Output: Albert Hitchconsole.log(vm.user.name) // Output: Sam Lee (Not Updated)console.log(vm.userName) // Output: Sam Lee (Not Updated)
If Object.assign
on vm.user
without creating a fresh object, computed
shall not be updated (same effect as not using Vue.set
).
const user = { id: 0}const vm = new Vue({ data: { user: user }, computed: { userId() { return this.user.id }, userName() { return this.user.name } }})vm.user = Object.assign(vm.user, { id: 7, name: 'Desmond Lua', height: 186 })console.log(user.id) // Output: 7console.log(vm.user.id) // Output: 7console.log(vm.userId) // Output: 7console.log(user.name) // Output: Desmond Luaconsole.log(vm.user.name) // Output: Desmond Luaconsole.log(vm.userName) // Output: Desmond Lua (First access cache)vm.user.name = 'Sam Lee'console.log(user.name) // Output: Sam Leeconsole.log(vm.user.name) // Output: Sam Leeconsole.log(vm.userName) // Output: Desmond Lua (Not Updated)user.name = 'Albert Hitch'console.log(user.name) // Output: Albert Hitchconsole.log(vm.user.name) // Output: Albert Hitchconsole.log(vm.userName) // Output: Desmond Lua (Not Updated)
If Object.assign
applied on original user
(not vm.user
) to create fresh object (using {}
), only the original user
object outside of Vue is updated (since it's a fresh object), wm.user
shall not be updated.
let user = { id: 0}const vm = new Vue({ data: { user: user }, computed: { userId() { return this.user.id }, userName() { return this.user.name } }})user = Object.assign({}, user, { id: 7, name: 'Desmond Lua', height: 186 })console.log(user.id) // Output: 7console.log(vm.user.id) // Output: 0 (Not Updated)console.log(vm.userId) // Output: 0 (Not Updated)console.log(user.name) // Output: Desmond Luaconsole.log(vm.user.name) // Output: undefined (Not Updated)console.log(vm.userName) // Output: undefined (Not Updated)vm.user.name = 'Sam Lee'console.log(user.name) // Output: Desmond Lua (Not Updated)console.log(vm.user.name) // Output: Sam Leeconsole.log(vm.userName) // Output: undefined (Not Updated)user.name = 'Albert Hitch'console.log(user.name) // Output: Albert Hitchconsole.log(vm.user.name) // Output: Sam Lee (Not Updated)console.log(vm.userName) // Output: undefined (Not Updated)
If Object.assign
on original user
(not vm.user
) without creating a fresh object, computed
shall not be updated (same effect as not using Vue.set
).
let user = { id: 0}const vm = new Vue({ data: { user: user }, computed: { userId() { return this.user.id }, userName() { return this.user.name } }})user = Object.assign(user, { id: 7, name: 'Desmond Lua', height: 186 })console.log(user.id) // Output: 7console.log(vm.user.id) // Output: 7console.log(vm.userId) // Output: 7console.log(user.name) // Output: Desmond Luaconsole.log(vm.user.name) // Output: Desmond Luaconsole.log(vm.userName) // Output: Desmond Lua (First access cache)vm.user.name = 'Sam Lee'console.log(user.name) // Output: Sam Leeconsole.log(vm.user.name) // Output: Sam Leeconsole.log(vm.userName) // Output: Desmond Lua (Not Updated)user.name = 'Albert Hitch'console.log(user.name) // Output: Albert Hitchconsole.log(vm.user.name) // Output: Albert Hitchconsole.log(vm.userName) // Output: Desmond Lua (Not Updated)
Conclusion: Follow Vue.js recommendedation to apply Object.assign
applied on vm.user
(not original user
) to create fresh object (using {}
) is the best choice, but it won't update the original user
. If you need to update the original user
as well, you should either:
- Pre-declared all object properties
- Use
Vue.set
instead ofObject.assign
- Update original
user
using Event Bus
References: