If we’re making changes to reactive properties in Vue apps that aren’t picked by Vue automatically, then we need to use the Vue.set
method.
In this article, we’ll take a look at reactive and non-reactive changes and how we can use Vue.set
with non-reactive changes.
Reactivity
Changes for reactive variables are tracked by defining traversing objects and defining getters and setters for them.
They’re used for tracking dependencies and notify Vue of changes.
Every component has a corresponding watcher instance to watch for property changes.
Each time a dependency setter is triggered, the watcher is notified, and cause the component to re-render.
Objects
Vue can’t detect property addition or deletion.
This is because the getter and setter conversion process requires the property to be present so that Vue can make it reactive.
For example, if we have:
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const app = new Vue({
el: "#app",
data: {
a: 1
}
});
app.b = 2;
</script>
</body>
</html>
Then the b
property won’t be watched automatically.
We can’t add properties to the root level of an already created instance.
But we can add new properties to a nested property with Vue.set
.
For example, we can write:
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{someObject.b}}
</div>
<script>
const app = new Vue({
el: "#app",
data: {
someObject: {}
}
});
Vue.set(app.someObject, "b", 2);
</script>
</body>
</html>
We call Vue.set
with the property with the object to add the property to.
Then the property name, and then the value.
We can also use the this.$set
method in the Vue instance object itself.
For instance, we can write:
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{someObject.b}}
</div>
<script>
const app = new Vue({
el: "#app",
data: {
someObject: {}
},
mounted() {
this.$set(this.someObject, "b", 2);
}
});
</script>
</body>
</html>
to do the same thing.
We can also use Object.assign
and assign it to the same property to add the property:
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{someObject.b}}
</div>
<script>
const app = new Vue({
el: "#app",
data: {
someObject: {}
},
mounted() {
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 });
}
});
</script>
</body>
</html>
Reassignment will also trigger Vue to re-render the component.
Arrays
Vue can’t detect some changes to arrays.
It can’t detect directly setting an item with the index.
And it can’t detect modifying the length
of the array.
So these 2 changes aren’t reactive:
vm.items[1] = 'x'
vm.items.length = 2
To make the changes, we can use Vue.set
again:
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{items[1]}}
</div>
<script>
const app = new Vue({
el: "#app",
data: {
items: ["foo", "bar"]
}
});
Vue.set(app.items, 1, "qux");
</script>
</body>
</html>
We call Vue.set
with the array property, index, and the value to set for the index.
We can also use splice
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{items[1]}}
</div>
<script>
const app = new Vue({
el: "#app",
data: {
items: ["foo", "bar"]
}
});
app.items.splice(1, 1, "qux");
</script>
</body>
</html>
The first argument is the index to change, the 2nd is how many items to change, and the 3rd is the value to change the entry with the given index to.
Also, we can call this.$set
in the component instance:
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{items[1]}}
</div>
<script>
const app = new Vue({
el: "#app",
data: {
items: ["foo", "bar"]
},
mounted() {
this.$set(this.items, 1, "qux");
}
});
</script>
</body>
</html>
this.$set
takes the same arguments as Vue.set
.
Conclusion
The Vue.set
method lets us make non-reactive object and array operations reactive.