How do deal with Reactivity Caveats
Challenge
Vue 2's Reactivity has a couple of caveats that need to be respected. Those are, in short:
export default {
data: () => ({
obj: {
existingProperty: 'value' // changing this value is reactive
}
array: ['Hello']
}),
created() {
this.existingProperty = 'new value' // reactive! :)
this.obj.newProperty = 'value' // not reactive :(
delete this.obj.existingProperty // not reactive :(
this.array[0] = 'Goodbye' // not reactive :(
this.array.length = 0 // not reactive :(
}
}
Vue 2 exposes helpers to deal with these caveats - this.$set()
and this.$delete()
:
export default {
data: () => ({
obj: {
existingProperty: 'value'
}
array: ['Hello']
}),
created() {
this.$set(this.obj, 'newProperty', 'value') // reactive :)
this.$delete(this.obj, 'existingProperty') // reactive :)
this.$set(this.array, 0,'Goodbye') // reactive :)
// special case: setting length never works, just replace with a new one.
this.array = [] // reactive :)
}
}
However, two things make this a bit of a challenge:
- We can't detect wrong usage for you with an eslint rule or similar. You have to watch out for these problems yourself.
- Vue 3 doesn't have these helper methods, but for Vue 2, you need them.
Recommendations
- Since
this.$set()
andthis.$delete()
are missing from Vue 3, but needed for Vue 2, we need replacements. - Write unit tests!
Options API: $set/$delete
Polyfill via @vue-bridge/runtime
Concerning Vue3, @vue-bridge/runtime
can help with the missing methods: When you define your component with its defineComponent
export will add these missing methods to the component when building our component for Vue 3.
import { defineComponent } from '@vue-bridge/runtime'
export default defineComponent({
data: () => ({
obj: {
existingProperty: 'value'
}
}),
created() {
// these helper methods will be provided
// for your component's Vue 3 version as well.
this.$set(this.obj, 'newProperty', 'value')
this.$delete(this.obj, 'existingProperty')
}
})
Composition API: set()/del()
polyfill via @vue-bridge/runtime
With Vue 2.7
, both Vue 2 and Vue 3 support the composition API. Vue 2 also exposes set()
and del()
helpers that work just like $set()
and $delete()
in Options API, but can be used in setup()
. Since Vue 3 doesn't expose them, use the ones provided by @vue-bridge/runtime
:
import { reactive } from 'vue'
import { defineComponent, set, del } from '@vue-bridge/runtime'
export default defineComponent({
data: () => ({
obj: {
existingProperty: 'value'
}
}),
setup() {
const obj = reactive({
existingProperty: 'value'
})
set(obj, 'newProperty', 'value')
del(obj, 'existingProperty')
}
})
Unit Testing for both Versions
You should test your components - obviously. And to make sure they behave the same in Vue 2 and Vue 3, we strongly recommend that you run your unit tests against both versions with the help of @vue-bridge/testing
.
This is of course a good recommendation for a lot of the cross-compatibility challenges we have documented in this guide, but it's especially important when it comes to the Reactivity which is central to Vue.