How Vue 3’s Proxy‑Based Reactivity Beats Vue 2’s Object.defineProperty

This article explains Vue’s reactive system, compares Vue 2.6’s Object.defineProperty approach with Vue 3’s Proxy implementation, details how observers are defined, collected, and triggered, and shows why the asynchronous update queue improves performance and maintainability.

WeChatFE
WeChatFE
WeChatFE
How Vue 3’s Proxy‑Based Reactivity Beats Vue 2’s Object.defineProperty

What is the reactive system

In Vue, a reactive system automatically re‑executes code when the data it depends on changes. When a template accesses a data property and that property is later mutated, Vue re‑renders the view.

The core idea is that a piece of code references a variable, and modifying that variable automatically triggers the code.

Vue distinguishes two roles: reactive data (the mutable state) and observers (the code that runs when the state changes).

Reactive data – the values you can modify.

Observer – the code executed after a data change.

Reactive observation of data

Two key questions arise: how do reactive data and observers connect, and how does a data change automatically trigger observers? The answer is proxy . Vue 3 intercepts get to collect dependencies and set to trigger them.

// proxy handler.get (simplified)
function get(target, key, receiver) {
  const res = Reflect.get(target, key, receiver);
  track(target, key); // collect observer as dependency
  return res;
}
// proxy handler.set (simplified)
function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  trigger(target, key, value); // trigger collected observers
  return result;
}

Thus, get collects dependencies via track, and set triggers them via trigger.

Why Vue 3 uses Proxy

Vue 2.x relied on Object.defineProperty for each property, requiring a traversal of all keys. Proxy needs only a single wrapper around the whole object, giving a time‑complexity advantage of n:1 for shallow observation.

var obj = { a: 999, b: 888, c: 777 };
// Using Object.defineProperty you must define a getter/setter for a, b, c individually.
// With Proxy you create one proxy for the whole object.

Additional benefits of Proxy include:

Support for adding/removing object keys.

Reactivity for Set and Map structures.

Array index and length tracking.

Definition of observers

Observers are the code that runs after a reactive change. Vue wraps this code in an effect to manage its interaction with reactive data.

Observer collection and triggering

Vue 3 creates a depsMap (a Map) for each observed target. The map’s keys are the target’s properties, and each key maps to a Set of dependent effects.

// collect dependency (simplified)
function track(target, type, key) {
  let depsMap = targetMap.get(target);
  if (!depsMap) targetMap.set(target, (depsMap = new Map()));
  let dep = depsMap.get(key);
  if (!dep) depsMap.set(key, (dep = new Set()));
  // ... add activeEffect to dep
}

When an observer runs, it becomes activeEffect and is pushed onto effectStack. Only the top of the stack is collected during a get operation.

// effect (observer) function (simplified)
const effect = function reactiveEffect() {
  try {
    enableTracking();
    effectStack.push(effect);
    activeEffect = effect;
    return fn(); // run the wrapped function (e.g., render)
  } finally {
    effectStack.pop();
    resetTracking();
    activeEffect = effectStack[effectStack.length - 1];
  }
};

The stack ensures proper ordering when one observer triggers another.

Observer categories

Computed property observers

Watch option observers

Render function observers

Computed observers act both as reactive data and as observers, and they are lazily evaluated. When a reactive property changes, its dependent computed observer is marked dirty, and the next render triggers its re‑evaluation.

Vue 2 vs Vue 3 observer definitions

Vue 2 uses a Watcher class; Vue 3 extracts shared logic into an effect function, improving maintainability.

// Vue 2 watcher run method (simplified)
function run() {
  if (this.active) {
    const value = this.get();
    if (value !== this.value || isObject(value) || this.deep) {
      const oldValue = this.value;
      this.value = value;
      if (this.user) {
        const info = `callback for watcher "${this.expression}"`;
        invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info);
      } else {
        this.cb.call(this.vm, value, oldValue);
      }
    }
  }
}
// Vue 3 effect import (simplified)
import { effect } from './effect';
class ComputedRefImpl {
  constructor(getter) {
    this.effect = effect(getter, {/* options */});
    // ...
  }
}

Asynchronous update queue

When a reactive change occurs, observers are not executed immediately. Vue batches them in an asynchronous queue so that each observer runs at most once per event loop, preserving a deterministic order.

In Vue 2, a queue stores watchers, deduplicates them, and flushes them via nextTick. The queue is sorted by the watcher’s creation id, giving earlier watchers higher priority.

// Vue 2 queue flushing (simplified)
function flushSchedulerQueue() {
  queue.sort((a, b) => a.id - b.id);
  queue.forEach(watcher => watcher.run());
}

The execution order becomes: computed observer → watch observer → render observer.

Vue 3 uses a similar mechanism: effects are placed into an async queue and cleared by a micro‑task ( flushJobs). Vue 3 further splits the queue into three categories for finer control.

Vue2 async update queue
Vue2 async update queue

Summary

From the perspective of the reactive system, the upgrade from Vue 2.6 to Vue 3.0 does not overturn the fundamental design; it still consists of data observation, observers, and an asynchronous update queue. The shift to Proxy, the extraction of observer logic into effect, and the refined async queue bring performance gains, better architecture, and easier extensibility for frontend developers.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performanceJavaScriptProxyVueVue3Reactivity
WeChatFE
Written by

WeChatFE

Tencent WeChat Public Platform Frontend Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.