Understanding Vue 2's Reactivity System

Reactivity Mechanism

Vue recursively traverses the object returned by data() and overrides its properties using Object.defineProperty() from ES5 to intercept read and write operations on these properties. For instance, if there's a property called username in data:

const originalValue = data.username;
Object.defineProperty(data, 'username', {
  get() {
    return originalValue;
  },
  set(newValue) {
    originalValue = newValue;
  }
});

After setting up interception for data, Vue employs the Observer Pattern to manage data change initiators and their dependencies. Note that Vue uses the Observer Pattern rather than Publish/Subscribe, which involves an additional "broker" component.

Simplified Vue Implementation

Observer/Dependency

class Dep {
  static target = null;
  subs = [];

  depend() {
    if (Dep.target) {
      this.subs.push(Dep.target);
    }
  }

  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

Watcher

class Watcher {
  data;
  key;
  oldVal;
  callback;

  constructor(data, key, callback) {
    this.data = data;
    this.key = key;
    this.callback = callback;
    
    Dep.target = this;
    this.oldVal = this.data[this.key];
    Dep.target = null;
  }

  update() {
    const newVal = this.data[this.key];
    if (newVal !== this.oldVal) {
      this.callback(newVal, this.oldVal);
      this.oldVal = newVal;
    }
  }
}

Vue Class

class MyVue {
  constructor(data, inputs, outputs) {
    this.data = data;

    Object.keys(data).forEach(key => {
      const dep = new Dep();
      let val = data[key];

      Object.defineProperty(data, key, {
        get() {
          dep.depend();
          return val;
        },
        set(newVal) {
          val = newVal;
          dep.notify();
        }
      });
    });

    inputs.forEach(input => {
      input.ele.addEventListener(input.event, e => {
        this.data[input.key] = e.target.value;
      });
    });

    outputs.forEach(output => {
      output.ele[output.attr] = this.data[output.key];
      new Watcher(this.data, output.key, value => {
        output.ele[output.attr] = value;
      });
    });
  }
}

Test Example

window.onload = function() {
  const data = { name: 'initial value' };

  const inputs = [
    {
      key: 'name',
      ele: document.querySelector('#input'),
      event: 'input'
    }
  ];

  const outputs = [
    {
      key: 'name',
      ele: document.querySelector('#output'),
      attr: 'innerHTML'
    }
  ];

  const myVue = new MyVue(data, inputs, outputs);
};
<div id="output"></div>
<input id="input" />

Tags: vue Reactivity observer-pattern javascript Framework

Posted on Wed, 02 Sep 2026 16:16:28 +0000 by dave914