Those of you who have read the official VUE documentation should already be familiar with this diagram.

How is the responsiveness of VUE implemented?

I’ve heard too many answers, through Object.defineProperty, but when asked in detail, the other party didn’t know.

Lu to respect first

const Observer = function(data) {
  // Loop change to add get set for each attribute
  for (let key indata) { defineReactive(data, key); }}const defineReactive = function(obj, key) {
  // the local variable dep is used for the get set internal call
  const dep = new Dep();
  // Get the current value
  let val = obj[key];
  Object.defineProperty(obj, key, {
    // Sets the current description property to recyclable
    enumerable: true.// Set the current description property to be modified
    configurable: true,
    get() {
      console.log('in get');
      // Call addSub in the dependency collector to collect the dependencies of the current property and Watcher
      dep.depend();
      return val;
    },
    set(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;
      // When the value changes, notify the dependent collector, update each Watcher that needs to be updated,
      // Where does each need to be updated by what determination? dep.subsdep.notify(); }}); }const observe = function(data) {
  return new Observer(data);
}

const Vue = function(options) {
  const self = this;
  // Assign data to this._data, the source code part of the Proxy so we use the simplest temporary implementation
  if (options && typeof options.data === 'function') {
    this._data = options.data.apply(this);
  }
  // Mount function
  this.mount = function() {
    new Watcher(self, self.render);
  }
  // Render function
  this.render = function() {
    with(self) { _data.text; }}/ / to monitor this. _data while forming
  observe(this._data);  
}

const Watcher = function(vm, fn) {
  const self = this;
  this.vm = vm;
  // Point the current dep. target to yourself
  Dep.target = this;
  // Add the current Wathcer to the Dep method
  this.addDep = function(dep) {
    dep.addSub(self);
  }
  // Update method used to trigger vm._render
  this.update = function() {
    console.log('in watcher update');
    fn();
  }
  Vm. _render is called for the first time to trigger get for text
  // To associate the current Wathcer with the Dep
  this.value = fn();
  // Dep. Target is cleared to prevent notify from repeatedly binding Watcher to Dep.
  // Cause code to loop forever
  Dep.target = null;
}

const Dep = function() {
  const self = this;
  // Collect the target
  this.target = null;
  // Stores the Watcher that needs to be notified in the collector
  this.subs = [];
  // Bind the relationship between Dep and Wathcer when there is a target
  this.depend = function() {
    if (Dep.target) {
      // Self.addSub (dep.target),
      // I didn't write this because I wanted to restore the source code process.Dep.target.addDep(self); }}// Add Watcher for the current collector
  this.addSub = function(watcher) {
    self.subs.push(watcher);
  }
  // Notify all wathcers in the collector to call their update method
  this.notify = function() {
    for (let i = 0; i < self.subs.length; i += 1) { self.subs[i].update(); }}}const vue = new Vue({
  data() {
    return {
      text: 'hello world'
    };
  }
})

vue.mount(); // in get
vue._data.text = '123'; // in watcher update /n in get
Copy the code

Here we have implemented a simple VUE response in less than 100 lines of code. Of course, if you don’t think about the process, I’m sure you can do it in 40 lines of code. But I don’t want to omit it here. Why? I am afraid that you will automatically ignore the process, and people will ask you something about it, but they will be speechless when they read it. Anyway, FOR your own good, drink plenty of hot water.

What does Dep do?

Dependency collector, this is not the official name clam, I named it myself, just to make it easier to remember.

Let’s look at two examples of dependency collectors in action.

  • Example 1: Is rendering meaningless unnecessary?

    const vm = new Vue({
        data() {
            return {
                text: 'hello world'.text2: 'hey',}}})Copy the code

    Text2 is called again when the vm.text2 value changes, but text2 is not used in the template, so is it pointless to handle render here?

    In the render function of Vue, we call the value that is relevant to the render. Therefore, values that are not related to the render will not trigger get and will not be added to the listener in the dependency collector (addSub does not trigger). Subs in notify is also empty. OK, go back to the regression demo, let’s do a little test to confirm what I said.

    const vue = new Vue({
      data() {
        return {
          text: 'hello world'.text2: 'hey'
        };
      }
    })
    
    vue.mount(); // in get
    vue._data.text = '456'; // in watcher update /n in get
    vue._data.text2 = '123'; // nothing
    Copy the code
  • Example 2: Who is notified when multiple Vue instances reference the same data? Should we call both of them?

    
    let commonData = {
      text: 'hello world'
    };
    
    const vm1 = new Vue({
      data() {
        returncommonData; }})const vm2 = new Vue({
      data() {
        return commonData;
      }
    })
    
    vm1.mount(); // in get
    vm2.mount(); // in get
    commonData.text = 'hey' In watcher update /n in get
    Copy the code

I hope that through these two examples, you have a general understanding of the function of Dep. Do you have a feeling that it is the same as before? I wish I had. To recap (the following dependency collector is actually Dep) :

  • vuewilldataInitialize to aObserverAnd for every value in the object, we overwrite the valueget,set.dataEach of thekeyEach has a separate dependent collector.
  • ingetAdd a listener to the dependent collector
  • At mount, an instance was createdWatcher, which points the target of the collector to the presentWatcher
  • indataTriggered when the value changesset, triggers updates that depend on all listeners in the collectorWatcher.update

If that’s not enough, check out my other articles

“Hand in hand with you through the VUE part of the source code”

What vue did to Template