Frontend Development 14 min read

Understanding Vue.js Data Observation, Reactivity, and Component Architecture

This article explains why Vue.js is chosen over other MVVM frameworks, details its data observer mechanism with source code examples, and demonstrates component-based architecture in a real-world project, highlighting Vue's reactivity, watcher system, and future prospects.

Qunar Tech Salon
Qunar Tech Salon
Qunar Tech Salon
Understanding Vue.js Data Observation, Reactivity, and Component Architecture

In the current booming era of front‑end development, many MVVM frameworks such as Vue, React, and Angular compete for adoption. This article discusses why Vue is selected over its peers, examines its data observation mechanism from source code, and shows how Vue components are organized in a large project.

1. Why use Vue

Vue offers a clear API and documentation written by a Chinese developer, making it easier for local developers to understand without heavy reliance on translated foreign docs. Its entry point is beginner‑friendly for developers unfamiliar with Node, npm, webpack, or ES6, and its template syntax is intuitive for those used to traditional templating.

Compared with React’s JSX, Vue avoids the need to rewrite class and for attributes, and unlike Angular’s dirty‑checking, Vue uses Object.defineProperty to precisely map data changes to the view, offering better performance on modern browsers.

2. Vue’s data observer pattern – source code walk‑through

var data = { a: 1 };
var vm = new Vue({ data: data })
vm.$watch('a', function () { console.log('the data a value changed') })
vm.a = 2;

When the property a changes, the console logs the message, demonstrating Vue’s reactivity.

In Vue 2.2.4 the core observer implementation looks like this:

function Observer (value) {
  this.value = value;
  this.dep = new Dep();
  // walk through each property and convert them into getter/setters
  this.walk(value);
}

The Observer class adds a getter and setter to each property using Object.defineProperty . When a property changes, it notifies its Dep (dependency) object.

function defineReactive$$1 (obj, key, val, customSetter) {
  var dep = new Dep();
  var property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
    return;
  }
  var getter = property && property.get;
  var setter = property && property.set;
  var childOb = observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
        }
      }
      return value;
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val;
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return;
      }
      if (customSetter) {
        customSetter();
      }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = observe(newVal);
      dep.notify();
    }
  });
}

The set method calls dep.notify() , which triggers all subscribed watchers.

var Dep = function Dep () {
  this.id = uid$1++;
  this.subs = [];
};
Dep.prototype.addSub = function addSub (sub) {
  this.subs.push(sub);
};
Dep.prototype.removeSub = function removeSub (sub) {
  remove(this.subs, sub);
};
Dep.prototype.depend = function depend () {
  if (Dep.target) {
    Dep.target.addDep(this);
  }
};
Dep.prototype.notify = function notify () {
  var subs = this.subs.slice();
  for (var i = 0, l = subs.length; i < l; i++) {
    subs[i].update();
  }
};

A Watcher parses an expression, collects dependencies, and runs a callback when the value changes:

var Watcher = function Watcher (vm, expOrFn, cb, options) {
  this.vm = vm;
  vm._watchers.push(this);
  this.cb = cb;
  this.id = ++uid$2;
  this.deep = options && !!options.deep;
  this.user = options && !!options.user;
  this.lazy = options && !!options.lazy;
  this.sync = options && !!options.sync;
  this.getter = typeof expOrFn === 'function' ? expOrFn : parsePath(expOrFn);
  this.value = this.lazy ? undefined : this.get();
};
Watcher.prototype.run = function run () {
  if (this.active) {
    var value = this.get();
    if (value !== this.value || isObject(value) || this.deep) {
      var oldValue = this.value;
      this.value = value;
      if (this.user) {
        try {
          this.cb.call(this.vm, value, oldValue);
        } catch (e) {
          handleError(e, this.vm, "callback for watcher \"" + this.expression + "\"");
        }
      } else {
        this.cb.call(this.vm, value, oldValue);
      }
    }
  }
};

When an object property changes, the watcher’s callback is invoked, completing the reactivity loop.

3. Vue in a real project

In a typical team, each page may be handled by a different developer. Vue’s component model allows finer granularity: a page is composed of many independent components, each managing its own slice of the shared model.

<div id="mainPage">
  <map-photo-cp v-on:detailRefresh="refreshDetail"></map-photo-cp>
  <order-state-cp></order-state-cp>
  <distanse-info-cp></distanse-info-cp>
  <price-info-cp></price-info-cp>
  <driver-info-cp></driver-info-cp>
  <goods-package-cp></goods-package-cp>
  <privileges-info-cp></privileges-info-cp>
  <transfer-recommend-cp></transfer-recommend-cp>
  <order-oprate-cp></order-oprate-cp>
  <pay-cp></pay-cp>
</div>

Each component is defined by extending a base component, providing its own template, lifecycle hooks, methods, and data:

var mapPhotoCp = Vue.extend({
  extends: baseCp,
  template: template,
  created: function () {},
  methods: {
    onOrderDetailReady: function (data) {},
    initMapLink: function () {},
    statusInArray: function (status, array) {},
    tap: function () {}
  },
  data: function () {}
});
Vue.component('map-photo-cp', mapPhotoCp);

When the Vue instance is created, it registers these components globally, parses the DOM, and binds each component’s model to its view. Any change in the shared model automatically updates the corresponding component’s UI, eliminating manual DOM manipulation.

4. Outlook

Vue continues to evolve, adding features such as Virtual DOM for efficient updates and Server‑Side Rendering (SSR) for faster first‑paint performance, positioning it as a modern, high‑performance MVVM framework.

Frontend Developmentcomponent architectureVue.jsObserver PatternReactivity
Qunar Tech Salon
Written by

Qunar Tech Salon

Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.

0 followers
Reader feedback

How this landed with the community

login 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.