Consider the following code

new Vue({
  el: '#example'.data() {return{obj:{a: 1}}},})Copy the code

When we write this line of code, VUE does dependency tracing on obJ objects we define in data.

Do this by implementing the New Observer(OBJ)

// After the above code, Obj :{obj:{a:1, __ob__:{//Observer instance dep:{dep instance subs:[// New Watcher(), new Watcher(), new Watcher(), new Watcher(), ] } } } }Copy the code

So let’s do that step by step.

  1. inobjAdd to object__ob__Property with a value ofObserveClass, we’ll write onedefFunction to add attributes
functiondef(obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !! enumerable, writable:true,
    configurable: true
  });
}

Copy the code

What attributes are added? As mentioned earlier, we need to add an Observer instance as follows

Observe implementation

class Observer {
  constructor(targetObject) {
    def(targetObject, '__ob__', this); // Add an Observer instance to targetObject, This.walk (targetObject) this.dep = new dep ()} walk(obj) {object.keys (obj).foreach (key => { defineReactive(obj, key, obj[key]) }); }}function observe(data) {
  if(Object.prototype.toString.call(data) ! = ='[object Object]') {
    return
  }
  new Observer(data)
}

function defineReactive(obj, key, val) {
  observe(val)

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      console.log('get');
      const ob = this.__ob__
      ob.dep.depend();
      return val
    },
    set: function reactiveSetter(newVal) {
      console.log('set');
      if (newVal === val) returnval = newVal observe(newVal) const ob = this.__ob__ ob.dep.notify(); }})}functiondef(obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !! enumerable, writable:true,
    configurable: true
  });
}
Copy the code

There’s a Dep involved, and we’ll implement Dep as well,

Dep

class Dep {
  constructor() {
    this.subs = []
  }

  addSub(sub) {
    this.subs.push(sub)
  }

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

  notify() {
    for (let i = 0; i < this.subs.length; i++) {
      this.subs[i].fn()
    }
  }
}

Dep.target = null


Copy the code

The Observer class primarily does the following

  1. Iterate over each property under data or, in the case of an object, execute new Observer() to add it to the object__ob__Property whose value is an instance of an Observer
  2. Hijack object property changes, in the getter, get the DEP instance of the Observer instance, and execute dep.depend() as follows
  const ob = this.__ob__
  ob.dep.depend();
Copy the code

Take a look at what dep.depend() does

this.subs.push(Dep.target)
Copy the code

Add dep. target to the subscription array (this.subs)

That is, as soon as dep.target is assigned and dep.depend() is executed, the value will be added to the subs array of the Dep, e.g

Dep.target =function test(){}
dep.depend()
// testThe function is a subscriber to the DepCopy the code

Implement automatic addition of dependencies

This is where Watcher comes in

Watcher implementation

const Dep = require('./Dep') class Watcher { constructor(vm, exp, Fn) {this.vm = vm this.exp = exp this.fn = fn dep. target = this This.vm [exp]}} module.exports = WatcherCopy the code

To understand Watcher, consider a small example

const obj = {
  a: 1,
  b: {
    c: 2
  }
}

new Observer(obj)
new Watcher(obj, 'a', () => {
  console.log('Watcher callback execution ')
})
obj.a='222'

Copy the code

The process is as follows:

  1. First observe obJ objects (new Observer(obj))
  2. instantiationWatcherIs executedDep.target = thisAnd then executethis.vm[exp]That is, fetch a value once, which triggers the getter to add itself (the Watcher instance) to the DEP’s subscriber array
 get: function reactiveGetter() {
      const ob = this.__ob__
      ob.dep.depend();
      return val
    },
Copy the code

And finally, when you change the data, you fire the setter

 set: function reactiveSetter(newVal) {
      if (newVal === val) return
      val = newVal
      observe(newVal)
      const ob = this.__ob__
      ob.dep.notify();
    },
Copy the code

Perform ob. Dep. Notify ()

 notify() {
    for (let i = 0; i < this.subs.length; i++) {
      this.subs[i].fn()
    }
Copy the code

Traversing the subscriber (subs) executes the callback function, and the process ends

The source code