Vue 3 Reactivity: Proxies vs. Vue 2 Reactivity

One of the biggest improvements in Vue 3 is its completely redesigned reactivity system. Whilst effective, Vue 2's reactivity system had some notable limitations, especially when working with arrays or dynamically adding properties. Vue 3 leverages JavaScript's built‑in Proxy feature to solve many of these issues.
In this article, I intend to explore the differences between Vue 2's reactivity approach and Vue 3's use of Proxies and explain how each system works and why Vue made the switch.
How Reactivity Worked in Vue 2
Before looking at Vue 3's improvements, it makes sense for us to briefly revisit how Vue 2's reactivity works.
Vue 2: Object.defineProperty()
In Vue 2, the reactivity system relied heavily on JavaScript's Object.defineProperty() method (which I've written about in the past here). This method defines getters and setters on object properties, allowing Vue to detect and respond to changes, like this:
const data = {};
Object.defineProperty(data, 'message', {
get() {
console.log('Accessing message');
return 'Hello';
},
set(newValue) {
console.log('Updating message to:', newValue);
},
});This small example only logs access and assignment; it is not a complete reactivity system. Vue 2 uses getters to track which properties a render depends on, and setters to notify those dependants when a value changes. Reading message does not itself mean the DOM needs updating.
Limitations of Vue 2 Reactivity
This approach has a few limitations:
- New properties added after initialisation are not reactive.
- Array modifications require special methods (
push,splice, etc.) to trigger updates. - It cannot detect changes to properties that weren't initially defined.
The awkward part was remembering which updates needed a helper. An ordinary‑looking assignment could change the data without notifying the view.
How Vue 3 Uses Proxies for Reactivity
Vue 3 uses JavaScript Proxy objects to intercept operations on a reactive object, including additions and deletions that Vue 2's initial getter‑and‑setter conversion could not observe directly.
What are JavaScript Proxies?
A Proxy is an object which wraps another object (the 'target') and then intercepts operations such as reading, writing, or deleting properties. It can then handle these operations or pass them through to the underlying object.
Here's a quick example:
const target = { message: 'Hello' };
const proxy = new Proxy(target, {
get(target, prop) {
console.log(`Property "${String(prop)}" read.`);
return target[prop];
},
set(target, prop, value) {
console.log(`Property "${String(prop)}" updated to "${value}"`);
target[prop] = value;
return true;
},
});
console.log(proxy.message); // logs "Hello"
proxy.message = 'Hi!'; // triggers setterThat gives Vue 3 a way to observe more kinds of operation. It does not mean that using a native Proxy makes every workload faster; performance also depends on Vue's implementation and the application being measured.
How Vue 3 Uses Proxies for Enhanced Reactivity
By adopting Proxies, Vue 3's reactivity system provides several advantages:
Dynamic Property Reactivity
Properties added after initialisation become reactive automatically, for example:
const state = Vue.reactive({});
// Adding a new reactive property dynamically
state.newMessage = 'Hello Vue!';This means that Vue 3 immediately tracks and responds to a new property, something that Vue 2 could not easily do.
Better Array Reactivity
Arrays wrapped in Vue 3's reactive() can track direct index assignment and changes to length. Those are useful differences from Vue 2; push() already triggered reactive updates there:
import { reactive } from 'vue';
const state = reactive({ items: ['a', 'b', 'c'] });
state.items[1] = 'updated'; // Direct index assignment is reactive
state.items.length = 1; // Changing the length is reactive tooIn Vue 2, an index replacement needed Vue.set() or splice(), and shortening an array needed splice(). Vue.set() is a framework helper, not an array method. Vue 3 lets us use the direct assignments shown above on the reactive array.
Comparing Vue 2 and Vue 3 Reactivity
To summarise, here's a brief overview of the main differences between the two approaches:
| Feature | Vue 2 Reactivity | Vue 3 Reactivity |
|---|---|---|
| Implementation | Getters/Setters | JavaScript Proxies |
| New Properties | Declare initially or use Vue.set() on a nested object | Add properties through the reactive proxy |
| Array Reactivity | Index and length assignment need reactive helpers | Index and length assignment are tracked on reactive arrays |
| Performance | Measure the application and workload | Measure the application and workload; Proxy alone is not a speed guarantee |
| Browser Support | Wider browser compatibility | Requires modern browsers |
Browser Support and Polyfills
Browser support needs a wider check than whether Proxy exists. Vue 3 requires modern JavaScript support and does not support Internet Explorer 11. Check the Vue browser requirements, the particular Vue release and the build's output target; the first browser versions to implement Proxy are not a complete Vue 3 support matrix.
The Proxy behaviour used by Vue 3 cannot be supplied faithfully by a polyfill. A legacy‑browser requirement therefore needs an explicit product decision rather than a routine fallback to Vue 2, which reached end of life on 31 December 2023. Existing Vue 2 applications should plan a migration or obtain maintained extended support; new work should use Vue 3.
Wrapping Up
Key Takeaways
- Vue 2 used getters and setters (
Object.defineProperty) to track changes, leading to certain limitations. - Vue 3 uses JavaScript proxies, offering improved flexibility and automatic reactivity.
- Property additions, direct array index assignment and array length changes are tracked when made through Vue 3's reactive proxies.
Proxylets Vue observe more operations; measure performance on the application rather than assuming a universal speed improvement.- Browser support for proxies is strong, but Vue 3's
Proxy‑based reactivity cannot be polyfilled for unsupported legacy browsers.
Vue 3 can observe property additions, deletions, arrays, and collection types more naturally because a Proxy intercepts operations on the object itself. Vue 2 had to convert known properties with getters and setters. The improvement is substantial, but destructuring reactive values and relying on object identity can still break expectations.