Handling New Property Addition in Vue 2.x Without Triggering Re-render
The Problem with Direct Property Addition
Consider a Vue component with a v-for directive:
<p v-for="(value, key) in item" :key="key">
{{ value }}
</p>
<button @click="addProperty">Add New Property</button>
With the following Vue instance:
const app = new Vue({
el: "#app",
data: () => ({
item: {
oldProperty: "Existing property"
}
}),
methods: {
addProperty() {
this.item.newProperty = "New property"
console.log(this.item) // Shows newProperty exists
}
}
})
The property gets added to the object but doesn't trigger a view update.
Technical Analysis
Vue 2.x uses Object.defineProperty for reactivity. When adding new properties dynamically, they aren't automatically made reactive:
const obj = {}
Object.defineProperty(obj, 'existingProp', {
get() {
console.log('Getter triggered');
return val
},
set(newVal) {
console.log('Setter triggered');
val = newVal
}
})
// This works with reactivity:
obj.existingProp
obj.existingProp = 'new value'
// This won't trigger reactivity:
obj.newProp = 'value'
Solutions
Vue.set()
Vue.set(targetObject, 'propertyName', value)
Intrenally, this calls defineReactive which uses Object.defineProperty to make the new property reactive.
Object.assign()
Create a new object with merged properties:
this.item = Object.assign({}, this.item, {
newProperty1: value1,
newProperty2: value2
})
$forceUpdate
As a last resort (not recommended for normal use):
this.$forceUpdate()
Recommendations
- For adding few properties: Use
Vue.set() - For adding multiple properties: Use
Object.assign() - Avoid
$forceUpdate()in most cases
Note: Vue 3's proxy-based reactivity system handles dynamic property additions automatically.