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.idwork as expected because it is a pre-declared propertyuser.weightwork as expected because we useVue.setto add property.user.agehad unexpected result forcomputed userAgebecause.ageproperty is added on runtime directly (not viaVue.set). If you already added a runtime property directlty, subsequent call toVue.sethas 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.setinstead ofObject.assign - Update original
userusing Event Bus
References: