“This is the 10th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

Previously on & Background

In the last article, we basically figured out what was going on with initProps. The key was to make all the props we configured on vm.$options.propsOptions responsive via defineReactive. The core is the observe method, and the core of the observe method is the observe class;

In addition, in the process of implementing responsive data, one of the most powerful people we’ve been talking about, but never met, is Dep, who relies on the collection and distribution of updates that we keep talking about, and here he is today.

A,,, the Observer

Class: the location of the SRC/core/observer/index. The js – > class to the observer

Class creates an instance of the observer class. Its constructor receives an observed object value, and appends the observer instance to value itself as the value of __ob__.

The observer object will convert the property of the target object value into getters and setters for collecting dependencies and issuing updates.

The constructor does the following:

  1. Initialize one on the Observer instanceDepExample, this is used for dependency collection;
  2. Under observationvalueAdd to object__ob__Property, the value isthis, the observer object itself;
  3. If the judgevalueIs an array, then rewrite the array prototype on the operation array method, in order to achieve the array response, then callthis.observeArray(value)Make the array item data responsive, that’s why the array doesn’t have onegettersetterThere can still be responsive root causes;
  4. ifvalueIs an objectthis.walk(value)Method traversal, takevalueAll of the child and descendant properties of theobserveI don’t see any recursion in theredefineReactiveCall, this step is implicitly done here
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number;

  constructor (value: any) {
    this.value = value

    // instantiate a dep
    this.dep = new Dep()
    this.vmCount = 0

    // Set the __ob__ attribute on the value object
    def(value, '__ob__'.this)
    if (Array.isArray(value)) {
      if (hasProto) {
        // Has a __proto__ attribute, enhanced by prototypes
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      // value is an object, recursively setting a response for each child property of the object
      this.walk(value)
    }
  }

  // Iterate over each key on the object, setting a response for each key
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      // This is equivalent to defining the response
      defineReactive(obj, keys[i])
    }
  }

  // Walk through the array, set the observation for each item in the array, handle the array elements as objects
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
Copy the code

1.1 protoAugment method

Methods location: SRC/core/observer/index. The js – > protoAument method: The target and SRC arguments are used to modify the __proto__ attribute reference of the target object. In the New Observer, when the value is an array, __proto__ of value points to an object that overrides array methods;

function protoAugment (target, src: Object) {
  target.__proto__ = src
}
Copy the code

1.2 copyAugment

Methods location: SRC/core/observer/index. The js – > copyAugment method: receiving target, SRC, keys, key in the keys, expand to the target key corresponds to the value of the SRC is [key];

function copyAugment (target: Object, src: Object, keys: Array<string>) {
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i]
    def(target, key, src[key])
  }
}
Copy the code

If the object has a __proto__ attribute, then protoAument is used. Why are we doing this? This is because __proto__ is not a standard attribute, and most likely not implemented in browsers such as Internet Explorer, so copyAument is needed to add responsivity to the array.

Second, the Dep

SRC /core/observer/dep.js -> class dep

Classes are used to collect dependencies when reading reactive data. Each key has its own DEP, and each child property has its own DEP. This dependency is collected by the Watcher, which watcher reads the key. When reactive data is updated, the update is reevaluated by calling the watcher.update() method.

export default class Dep {
  statictarget: ? Watcher;// dep. target is a Watcher value
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  // Push Watcher in dep.subs
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }
  
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  
  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if(process.env.NODE_ENV ! = ='production' && !config.async) {
    }
    // Go through the watcher stored in the DEP and execute watcher.update()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}
Copy the code

2.1 Dep. Prototype. Depend

This method is used to add deP to the WATcher and add watcher to the DEP. In the previous defineReactive method, called in the getter of the final Object.defineProperty,

Dep. Target is a static property of the Dep class. It is set to “Watcher” when instantiated. The method now assigns dep. target the current Watcher instance;

Export function defineReactive (params was ignored) {// Instantiate dep with key/dep const dep = new dep () let childOb =! shallow && observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: True, // get intercepts the obj[key] read operation get: function reactiveGetter () { if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() if (Array.isArray(value)) { dependArray(value) } } } return value }, set: function reactiveSetter (newVal) { } }) }Copy the code

2.3 Dep. Prototype. Notify

The notify method sends updates through the deP’s collection of watchers.update ().

The previous call to the setter in the final Object.defineProperty method in defineReactive called notify

Export function defineReactive (params was ignored) {// Instantiate dep with key/dep const dep = new dep () let childOb =! shallow && observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: Function reactiveSetter () {}, set: function reactiveSetter (newVal) {p.notify()}}Copy the code

Third, summary

Now that we’ve covered all the initProp process, let’s review it:

  1. Initialize thevm._propsProperties,
  2. thenfor intraversevm.$options.propOptionsObjects? It’s all us in herenew Vue()The transfer ofpropsObject,
  3. In traversal, throughvalidatePropMethod to get eachkeyIf this value is no longerpropsDataIs called during the processobserve(val)Method to observe the default value and convert it into a responsive data structure:
    • The observe constructor is used to create an observe instance.

      • 3.1.1 will givevalinstantiationdepattribute
      • 3.1.2 and givevaladd__ob__Property, the value isObserverThe instance itself
      • 3.1.3 If val is an array, passprotoAument/copyAumentThe overwrite array method implements array responsivity and then callsthis.observerArrayImplement the response of the array subitem
      • 3.1.4 ifvalIs an object, thenthis.walk()That callObserverPrototype methodwalktraverseval, the core of which is recursive invocationdefineReactive()Method to turn the underlying subattributes of the object, such as subattributes, into responsive
    • If val is already an Observer instance, the new Observer instance will be returned

  4. calldefineReactive(obj, key, value)By means ofObject.defineProperty()Method that intercepts the reading and setting of properties, collects dependencies when reading, and sends updates when setting:
    • 4.1 instantiationDepAn instance of thedep = new Dep()
    • 4.2 getobj[key]The original property describes the object and is then retrieved from the description objectgettersetter
    • 4.3 callchildObj = observe(val)Method,observeInside is instantiationObserverClass, and will be calleddefineReactiveMethod, which in this case is an implicit recursive call to the defineReactive method, which implements a response for all nested child attributes
    • 4.4 callObject.definePropertyMethod, redefineobj[key]getset:
      • 4.4.1 the get:dep.depend().childObj.dep.denpend()That’s the quoteobj.keyobj.key.childKeyCan be listened to
      • 4.4.2 setAgain,observeThe new value, so that the new value is also reactive, and finallydep.notify()Send updates immediately