Recommended PC viewing, mobile code highlighting disorder
During the initialization phase of the Vue, the _init method is executed with the initState(VM) method:
// src/core/instance/state.js
export function initState (vm: Component) {
// ...
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
// ...
}
Copy the code
The initState method initializes properties such as props, methods, data, computed, and wathcer. Here we focus on props and data, and we’ll look at initialization of other properties in more detail later.
Here’s a flow chart:
The sections following the props section will look at this in more detail, but you just need to know that initProps called defineReactive
1. initProps
// src/core/instance/state.js
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
const keys = vm.$options._propKeys = []
constisRoot = ! vm.$parent
if(! isRoot) {
// Turn off the observing switch, observe will be invalid call
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
// A simplified version
defineReactive(props, key, value)
// Data broker, which occurs in the vue.extend phase for children of non-root instances
// This is an optimization that does not require calling Object.defineProperty for every component instance to implement the proxy
if(! (keyin vm)) {
proxy(vm, `_props`, key)
}
}
// Turn on the observation switch
toggleObserving(true)
}
Copy the code
The main procedure for initializing initProps is as follows:
- To turn off the observation switch, which will be described in a later chapter, here is a brief introduction:
- In fact, it’s a combination of
src/core/observer/index.js
In the fileshouldObserve
The global variable is set to zerofalse
. - This makes the
defineReactive
In the callobserve
Is an invalid call. - Because for the object
prop
Value of the child componentprop
The value always points to the parent componentprop
Value, as long as the parent componentprop
If the value changes, it triggers the re-rendering of the child component, so thisobserve
The process can be omitted.
- In fact, it’s a combination of
- Iterating over the definition
props
Configuration. The traversal process does two things:- One is to call
defineReactive
The method is to take eachprop
The corresponding value becomes responsive and can be passedvm._props.xxx
Access to definitionsprops
Property corresponding to. - The other is through
proxy
Put onvm.xxx
Access proxy tovm._props.xxx
On. One detail here is that for children of non-root instances, the proxy occurs onVue.extend
Phases, which will be covered in a later chapter.
- One is to call
- Turn on the switch for observation.
2. initData
// src/core/instance/state.js
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// ...
const keys = Object.keys(data)
// ...
let i = keys.length
while (i--) {
const key = keys[i]
// After simplifying...
proxy(vm, `_data`, key)
}
// observe data
observe(data, true /* asRootData */)
}
Copy the code
Initialization of initData does two things:
- traverse
data
Object, throughproxy
把vm.xxx
The agent tovm._data.xxx
上 - call
observe
Methods Observe the wholedata
The change of thedata
It also becomes reactive, and it goes throughvm._data.xxx
Access to definitionsdata
Property corresponding to.
As you can see, the initialization of both props and data is to turn them into reactive objects. We’ve touched on a couple of functions in this process, and we’ll look at them in detail.
Proxy has been introduced in the previous chapter, so it will not be introduced here.
3. observe
Observe function is used to the change of the monitoring data, it is defined in SRC/core/observer/index in js:
// src/core/observer/index.js
// Observe the switch
export let shouldObserve: boolean = true
export function toggleObserving (value: boolean) {
shouldObserve = value
}
export function observe (value: any, asRootData: ?boolean) :Observer | void {
if(! isObject(value) || valueinstanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve && // The observation switch is on
! isServerRendering() &&/ / the SSR
(Array.isArray(value) || isPlainObject(value)) && // Value is an array or a plain object
Object.isExtensible(value) && // value is extensible
! value._isVue// value is a non-vUE object
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
Copy the code
The observe method adds an Observer to the data of a non-VNode object type. If it has already been added, return it. Otherwise, instantiate an Observer under certain conditions (see comment). Let’s look at the Observer in action.
4. Observer
An Observer is a class that adds getters and setters to properties of an object for dependency collection and distribution of updates:
// src/core/observer/index.js
export class Observer {
value: any;
dep: Dep;
vmCount: number; // The number of VM instances with the current object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__'.this)
if (Array.isArray(value)) {
// ...
this.observeArray(value)
} else {
this.walk(value)
}
}
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
Copy the code
The Observer constructor logic is simple, first instantiating the Dep object, which is used for vue.set, as described in a later section.
We then add an instance of ourselves to the __ob__ attribute of the value of the data object by executing def. Def is defined in SRC /core/util/lang.js:
// src/core/util/lang.js
export function def (obj: Object, key: string, val: any, enumerable? :boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !! enumerable,
writable: true.
configurable: true
})
}
Copy the code
value
There’s an extra one on the object__ob__
Property to point toObserver
Instance.- At the same time
enumerable
The default setting forfalse
, so that whenfor
This property is not iterated over.
Returning to the Observer constructor, value is evaluated, observeArray is called for arrays, and walk is called for pure objects otherwise. ObserveArray iterates through the array and calls observe again. Walk iterates through the key of the object and calls defineReactive.
5. defineReactive
DefineReactive object function is to define a response type, add getter and setter to dynamic objects, it is defined in SRC/core/observer/index. In js:
// src/core/observer/index.js
export function defineReactive (
obj: Object.
key: string.
val: any.
customSetter?: ?Function.
shallow? :boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if((! getter || setter) &&arguments.length === 2) {
val = obj[key]
}
letchildOb = ! shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true.
configurable: true.
get: function reactiveGetter () {
// ...
return value
},
set: function reactiveSetter (newVal) {
// ...
}
})
}
Copy the code
DefineReactive initializes an instance of the Dep object, retrieves obJ’s property descriptor, and then recursively calls the Observe method on the child. This ensures that all of obJ’s child properties can become responsive objects, no matter how complex obJ’s structure is. This allows us to access or modify a deeply nested property in OBj and also trigger getters and setters. Finally, use Object.defineProperty to add getters and setters to obj’s property key. And the actual implementation of getters and setters, we’ll talk about later.
conclusion
If we have the following objects
{
a: 1.b: [2.3.4].c: {
d: 5}}Copy the code
After observation:
{
__ob__, Dep => Dep (uid:0)
a: 1.// DeP exists in defineReactive closure (uid:1)
b: [2.3.4].// Dep (uid:2) exists in defineReactive closure, and b.__ob__. Dep => dep(uid:3)
c: { // DeP exists in defineReactive closure (uid:4)
__ob__, Dep => Dep (uid:5)
d: 5 // Dep exists in the closure (uid:6)}}Copy the code