VueJS - Reactive Interface
Vue 3 - Deep Dive into Reactivity
Section titled “Vue 3 - Deep Dive into Reactivity”Vue’s core feature is its reactivity system. When you modify reactive data, Vue automatically detects the change and updates the DOM efficiently. Vue 3 significantly revamped the reactivity system using JavaScript Proxies, making it more powerful and consistent than Vue 2’s Object.defineProperty-based system.
This chapter explores how reactivity works in Vue 3, how to declare reactive state using ref and reactive, and how to handle reactivity caveats.
How Reactivity Works (Vue 3)
Section titled “How Reactivity Works (Vue 3)”- Proxies: When you declare state using
ref()orreactive(), Vue wraps your data (primitives or objects) in a JavaScript Proxy. - Tracking: When a component’s render function executes, Vue tracks which reactive properties are accessed (the ‘get’ trap of the Proxy).
- Triggering: When a reactive property is modified (the ‘set’ trap of the Proxy), Vue checks which components depend on that property (tracked during rendering) and triggers them to re-render.
This Proxy-based system allows Vue 3 to track property additions and deletions for objects declared with reactive, overcoming limitations present in Vue 2.
Declaring Reactive State
Section titled “Declaring Reactive State”ref() is used to create reactive variables for any value type, including primitives (String, Number, Boolean) and objects. It wraps the value in an object with a .value property.
import { ref } from 'vue';
// For primitivesconst counter = ref(0);const message = ref('Hello');const isActive = ref(true);
console.log(counter.value); // Access/modify using .value in JScounter.value++;
// For objectsconst user = ref({ name: 'Alice', age: 30 });user.value.age++; // Modify object property via .value
// In template, .value is automatically unwrapped:// <p>{{ counter }}</p>// <p>{{ user.name }}</p>Use ref when you need reactivity for standalone primitive values or when you might need to reassign the entire object/array.
reactive()
Section titled “reactive()”reactive() is used to create a reactive proxy specifically for objects (including arrays and built-in types like Map, Set). It makes the object itself deeply reactive.
import { reactive } from 'vue';
const state = reactive({ count: 0, user: { name: 'Bob', isAdmin: false }, items: ['apple', 'banana']});
// Access/modify directly in JSstate.count++;state.user.isAdmin = true;state.items.push('orange'); // Array mutations are reactive
// In template:// <p>{{ state.count }}</p>// <p>{{ state.user.name }}</p>Use reactive for grouping related data within a single object. Be aware that destructuring properties from a reactive object will cause them to lose reactivity unless you use utilities like toRefs.
ref vs reactive
Section titled “ref vs reactive”- Use
reffor primitives and when you might reassign the whole variable. - Use
reactivefor complex objects where you’ll primarily modify properties within the object. refrequires.valuein script,reactivedoes not.- Both work seamlessly in templates (refs are auto-unwrapped).
Reactivity with Arrays and Objects
Section titled “Reactivity with Arrays and Objects”Vue 3’s Proxy-based system handles most array and object manipulations reactively:
Arrays
Section titled “Arrays”- Mutation methods like
push,pop,shift,unshift,splice,sort,reverseare tracked. - Directly setting an element by index (e.g.,
myArray.value[0] = newValue) is reactive. - Modifying the
.lengthproperty is reactive.
Objects
Section titled “Objects”- Modifying existing properties is reactive.
- Adding new properties to an object declared with
reactive()is reactive in Vue 3 (e.g.,state.newProp = 'some value'). This is a major improvement over Vue 2. - Deleting properties using the
deleteoperator (e.g.,delete state.oldProp) is reactive for objects declared withreactive().
Important Note: If you use ref() with an object (e.g., const myObj = ref({ a: 1 })), adding a new root-level property directly (myObj.value.b = 2) is reactive. Deleting (delete myObj.value.a) is also reactive.
The need for Vue 2’s Vue.set and Vue.delete is eliminated in Vue 3 due to the capabilities of Proxies.
Watching Reactive Changes
Section titled “Watching Reactive Changes”Instead of manually watching properties outside the instance like vm.$watch in Vue 2, use the watch or watchEffect functions from the Composition API. These provide more explicit and powerful ways to react to state changes.
import { ref, watch, watchEffect } from 'vue';
const count = ref(0);const data = reactive({ name: 'test', value: 10 });
// Watch a specific refwatch(count, (newValue, oldValue) => { console.log(`Count changed from ${oldValue} to ${newValue}`);});
// Watch a getter function for a reactive propertywatch(() => data.value, (newValue) => { console.log(`Data value changed to ${newValue}`);});
// Watch multiple sourceswatch([count, () => data.value], ([newCount, newDataValue], [oldCount, oldDataValue]) => { console.log('Count or data.value changed');});
// watchEffect runs immediately and re-runs whenever its dependencies changewatchEffect(() => { // Tracks count and data.name as dependencies automatically console.log(`Current count: ${count.value}, Current name: ${data.name}`);});
// Example usagesetTimeout(() => { count.value = 5; data.value = 20; data.name = 'updated';}, 1000);watch provides fine-grained control over what to watch and gives access to old/new values. watchEffect is simpler for side effects that depend on multiple reactive properties, automatically tracking dependencies.
Reactivity Caveats
Section titled “Reactivity Caveats”- Destructuring
reactive: Destructuring properties from areactiveobject will cause them to lose reactivity because the destructured variable holds a plain value, not the proxy. UsetoRefsortoRefto create refs for individual properties if needed. - import { reactive, toRefs } from ‘vue’;
const state = reactive({ foo: 1, bar: 2 });
// WRONG: foo and bar are not reactive here // let { foo, bar } = state;
// CORRECT: fooRef and barRef are refs, synced with state const { foo: fooRef, bar: barRef } = toRefs(state);
console.log(fooRef.value); // 1 state.foo++; console.log(fooRef.value); // 2
- Replacing a
reactiveobject: You cannot directly replace an entire object declared withreactive. If you need to replace the whole object, userefinstead. - import { reactive, ref } from ‘vue’;
let state = reactive({ count: 0 }); // WRONG: This breaks reactivity // state = reactive({ count: 1 });
// Instead, modify properties: // state.count = 1;
// OR: Use Object.assign if you need to merge objects // Object.assign(state, { count: 1, newProp: true });
// OR: Use ref if full replacement is needed let stateRef = ref({ count: 0 }); stateRef.value = { count: 1 }; // CORRECT: This works
Conclusion
Section titled “Conclusion”Vue 3’s reactivity system, powered by Proxies and the ref/reactive APIs, offers a robust and intuitive way to build dynamic user interfaces. Understanding how it works and choosing the right API (ref vs reactive) helps in writing efficient and maintainable Vue applications.
Explore the official documentation for more advanced topics: Reactivity Fundamentals and Reactivity in Depth.