This is the ninth day of my participation in the First Challenge 2022.

Previously on & Background

Last time we looked at what initProp does when the initState() method is called. The main job of initProps is to for in traverse propsOptions and make the prop reactive via defineReactive. Finally, put the responsive data on the VM. _props, and finally, delegate the prop to the VM.

In the above work, the data was changed into responsive data through defineReactive in the last article, and one of the important steps was to change the sub-level properties of prop or even grandchild properties into responsive data through Observe. This part can be separately mentioned, so this essay comes into being.

Observe the timing of the observing

Before starting the source code for Observe, there is a small detail that needs to be mentioned, which is when the Observe method is called in the initProps procedure. The observe method is called in two places in the initProps procedure:

1. validatePorp

Call de validatePorp when the for in loop of initProps gets the default value of prop;

export function validateProp (.) :any {
  / /...
  if (value === undefined) {
    value = getPropDefaultValue(vm, prop, key)
    / /...
    observe(value)
  }

  return value
}
Copy the code

2.2 In the defineReactive method

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function, shallow? : boolean) {
  / /...
  letchildOb = ! shallow && observe(val)/ /...
}
Copy the code

The first reason to call Observe in the validateProp method is that when the default value is an object or array, it is calling the factory function to get a new data that has not yet been processed into responsive data. This call to Observe is to process the default value into responsive data.

The second is in defineReactive, which is a continuation of the first step. There is no conflict between the two steps. The first is that validate is not handled when prop is not the default value, but the value obtained from propsData. This value is either already reactive or unprocessed, and the second part of defineReactive is needed to process it.

Observe also has a logic to avoid reprocessing. If it does, it returns the result immediately. The source code explains this process in more detail.

Observe methods

3.1 Method Location:

src/core/observer/index.js -> observe

3.2 Functions of the method:

Create an instance of the Observer class for value. If the value has been created successfully or if the value has been created before, return the instance.

  1. If I get it rightvalueThe argument is not an object or isVNodeInstances are not handled
  2. Sentence reprobation, ifvalueExists on an object__ob__Property, indicating that the object has been processed as reactive, and returns a reactive result
  3. Create an observer instance and return it

Observer classes and observer instances will be covered in detail in our next topic.

export function observe (value: any, asRootData: ? boolean) :Observer | void {
  // If it is not an object or a VNode instance, exit without processing
  if(! isObject(value) || valueinstanceof VNode) {
    return
  }

  let ob: Observer | void

  If the value object has an __ob__ attribute, it indicates that it has been created for vlaue.
  // Return the __ob__ attribute directly
  // If the observer object is successfully created for a value, an __ob__ attribute is added to the value.
  // The value is the observer instance itself
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if( shouldObserve && ! isServerRendering() && (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) && ! value._isVue ) {// Create an observer instance
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}
Copy the code

Four, defineReactive

4.1 Method Location:

src/core/observer/index.js -> defineReactive

4.2 Functions of the method:

Defining a reactive property on an object, the core of which is the well-known obeject.definePorperty method, is implemented by the following steps:

  1. Instantiate aDepYou can see it in the frontfor intraversepropsOptionsTo eachkeyAll set to reactive, so eachpropThere is aDep;
  2. throughObject.getOwnPropertyDescriptor(obj, key), get thispropCorresponding to the original attribute description object, the so-called attribute description object isObject.definePropertyThe third argument to define the property behavior of the object, if describing the object’sconfigurablefalsereturnExit because this object is not configurable;
  3. Gets the corresponding key from the original property description objectgettersetter;
  4. Is called if the value of this property is still an objectobserve(val),childObjFor later dependency collection, there are two cases:
    • 4.1 ifvalHas already been treated as response four data when the default value was processed earlierval.__ob__Property, and the value of this is theta__ob__ObserverIs used to implement the dependency collection and data update when the update signal;
    • 4.2 ifvalIf it has not been processed, it is processed recursivelyvalProperty and its children, and finally returnsObserverInstance;
  5. throughObject.definePropertySet response type;

When the value is read, the getter is triggered, and the dependency is collected. This is to see which watcher uses the value of obj[key]. The deP records the value. And then when you set it, it’s going to fire the setter, and then it’s going to tell the DEP to tell the Watcher to go ahead and get the value of this object again, and that’s when you get the updated value.

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function, shallow? : boolean) {
  // Instantiate the dep, one deP per key, which is called by initProp. Key is a property in propOptions
  // If prop is an object, observe that the key of the child object is also created
  const dep = new Dep()

  // Get the original attribute description object of obj[key]. Return if it cannot be configured
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // Save the getter and setter in the property description object, the getter is used to get the value of val
  const getter = property && property.get
  const setter = property && property.set
  if((! getter || setter) &&arguments.length === 2) {
    // If no setter or getter is defined and only two parameters are passed, val is assigned obj[key]
    val = obj[key]
  }

  // Handle child attributes and even deeper nested attributes with observe recursion
  letchildOb = ! shallow && observe(val)// Responsive core
  Object.defineProperty(obj, key, {
    enumerable: true.configurable: true.// get intercepts the obj[key] read operation
    get: function reactiveGetter () {
      // Call the getter to get value omit...
      if (Dep.target) {
        // The depend method depends on collecting, adding watcher to the deP, and adding deP to the watcher
        dep.depend()

        // childObj represents the observer object of the neutron object of the object, and if it exists, it is also dependent on collection
        if (childOb) {
          // This is why prop.key.childKey can also be responsive when updated
          childOb.dep.depend()
        }
      }
      return value
    },

    // set intercepts the set operation on obj[key]
    set: function reactiveSetter (newVal) {
      / /...
      // observe new value, change new value to responsive datachildOb = ! shallow && observe(newVal) dep.notify()// Send updates}})}Copy the code

Five, the summary

This article discusses in detail two important methods in initProps: observe and defineReactive.

Observe (val) : create an Observer instance when val is an object or an array. The core of an Observer is to extend the __ob__ attribute to val and recursively call defineReactive.

DefineReactive core Object. DefineProperty intercepts the reading and setting of val’s properties, collecting dependencies when reading and sending updates when setting.

We have completed initState/initProps/observe and defineProperty. We have completed initProps/observe and defineProperty. Now the pit for initProps is not completely filled because there are two important things: the Observer and Dep, which will be discussed in the next topic;

It is my first time to try to write such a long series of articles. I have written all the tutorials before. This is also challenging myself, I hope the officials in front of the screen also challenge themselves, come on!