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.
- If I get it right
value
The argument is not an object or isVNode
Instances are not handled - Sentence reprobation, if
value
Exists on an object__ob__
Property, indicating that the object has been processed as reactive, and returns a reactive result - 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:
- Instantiate a
Dep
You can see it in the frontfor in
traversepropsOptions
To eachkey
All set to reactive, so eachprop
There is aDep
; - through
Object.getOwnPropertyDescriptor(obj, key)
, get thisprop
Corresponding to the original attribute description object, the so-called attribute description object isObject.defineProperty
The third argument to define the property behavior of the object, if describing the object’sconfigurable
为false
则return
Exit because this object is not configurable; - Gets the corresponding key from the original property description object
getter
和setter
; - Is called if the value of this property is still an object
observe(val)
,childObj
For later dependency collection, there are two cases:- 4.1 if
val
Has already been treated as response four data when the default value was processed earlierval.__ob__
Property, and the value of this is theta__ob__
是Observer
Is used to implement the dependency collection and data update when the update signal; - 4.2 if
val
If it has not been processed, it is processed recursivelyval
Property and its children, and finally returnsObserver
Instance;
- 4.1 if
- through
Object.defineProperty
Set 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!