Start with initProps to understand the responsive principle of VUE2
In vue2 initialize a vue instance will call this. _init (options) method (code is located in the SRC/core/instance/index. Js), Enclosing _init function in initMixin function (code is located in the SRC/core/instance/init. Js) in the extension, _init values for component state initialization function, is called initState function.
export function initMixin (Vue: Class<Component>) {
/ /... Omit other code
$parent * 2. $parent * 2. Push Vue instance to $children * 3. Mount Vue instance to $root * 4. Initialize the values of children, refs, _watcher, _inactive, _directInactive, * _isMounted, _isDestroyed, _isBeingDestroyed */
initLifecycle(vm)
/** * get the events attached to this instance from the parent component, and then update the component's event listener */
initEvents(vm)
/** * initialize the render function * 1. Mount the slot contents, slot and scopedslot to the instance * 2. Bind the createElement function to the instance (_c and $createElement) * 3. The $attrs and $Listeners properties are variable temperature response properties */
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
/** * props * 2. Initialization methods * 3. Initialize data * 4. Initialize computed * 5. Initialize watch */
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/ /... Omit other code
}
Copy the code
InitState function is located in the SRC/core/instance/state. The js file, it is started to initialize the props
export function initState (vm: Component) {
// Initialize the watcher array
vm._watchers = []
/ / get the options
const opts = vm.$options
// Initialize props
if (opts.props) initProps(vm, opts.props)
// Initialize methods
if (opts.methods) initMethods(vm, opts.methods)
// Initialize data
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)
if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) } }Copy the code
Now frominitProps
Function to begin to understand the responsive principle of VUE2
InitProps passes the props in options, caches the prop key, and then gets the prop value. Instead of fetching the prop value directly, the validateProp function is called
-
The validateProp function takes four arguments and returns the value of the corresponding prop
export function validateProp ( key: string, // prop key propOptions: Object.// Props for all props. PropsOptions) propsData: Object.// The prop data passed to this instancevm? : Component) :any Copy the code
PropsOptions comes from this.$options. Props, where the props are normalized by the normalizeProps function
The full definition of propOptions(not propsOptions) is:
type PropOptions = { type: Function | Array<Function> | null.default: any, required: ?boolean, validator:?Function }; Copy the code
validateProp
The main things I did were as follows:I. Obtain the complete configuration of the prop and the value passed in
Ii. When *prop expects type with Boolean * :
- If *
prop
The value is missing and not in the prop configurationdefault
= >value
A value offalse
* - If obtained
value
The value is an empty string or withkey
And with the sameprop
Not in the expectation typestring
orboolean
Higher priority= >The value evaluates to true
Iii. If the value is still undefined after the preceding two steps, obtain the default value from propOption and observe it
export function validateProp ( key: string, propOptions: Object, propsData: Object, vm? : Component) :any { // Get the complete configuration of the property const prop = propOptions[key] // Whether the value is missing constabsent = ! hasOwn(propsData, key)// Get the attribute value let value = propsData[key] // Boolean casting: Boolean value construct const booleanIndex = getTypeIndex(Boolean, prop.type)0 // Prop has Boolean in the expected type if (booleanIndex > -1) { // The prop value is missing and the prop configuration is not default. The value is false if(absent && ! hasOwn(prop,'default')) { value = false } else if (value === ' ' || value === hyphenate(key)) { // only cast empty string / same name to boolean if // boolean has higher priority const stringIndex = getTypeIndex(String, prop.type) // No string or Boolean type is expected to have a higher priority if (stringIndex < 0 || booleanIndex < stringIndex) { value = true}}}// Check default value Checks the default value if (value === undefined) { value = getPropDefaultValue(vm, prop, key) // since the default value is a fresh copy, // make sure to observe it. const prevShouldObserve = shouldObserve toggleObserving(true) observe(value) toggleObserving(prevShouldObserve) } if( process.env.NODE_ENV ! = ='production' && // skip validation for weex recycle-list child component props! (__WEEX__ && isObject(value) && ('@binding' in value)) ) { assertProp(prop, key, value, vm, absent) } return value } Copy the code
- If *
-
Let’s talk about that
obsever
functionThe contents of the observe function are simple, making several judgments about the value passed in:
- Non-objects or values are
VNode
Type ==> No processing is performed - Values are
observe
A = = > to returnvalue.__ob__
- Non-server rendering, value array or plain object, extensible, and not vUE instance ==> Create a new one
Observer
Instance and return
About the Observer class:
-
Constructor: Take a value parameter, save the value, create a Dep object, initialize the vmCount value, and if value is an array, The seven methods that change the array value (‘push’, ‘pop’, ‘shift’, ‘unshift’, ‘splice’, ‘sort’, ‘reverse’) will be extended in the constructor to make the changes to the array responsive. The observer function is also recursively executed for all the values in the array. For normal objects, the constructor iterates over all the values in value and defineReactive them.
-
Source:
export class Observer { value: any; dep: Dep; vmCount: number; // number of vms that have this object as root $data constructor (value: any) { this.value = value this.dep = new Dep() this.vmCount = 0 def(value, '__ob__'.this) / / extension array 'push', 'pop', 'shift', 'the unshift', 'splice', 'sort of', 'reverse' if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } this.observeArray(value) // Iterate through the values in the array } else { this.walk(value) // Iterate over the values in the object}}// This is the first time that I have been to the United States } Copy the code
-
The constructor of the Observer class also creates a new Dep object for dependency collection, which I’ll cover later
-
For normal JS objects, the Observer traversal calls the defineReactive function on the attributes in the object
The defineReactive function is used to make the data responsive. The way to do this is to use object.defineProperty function to intercept the get and set of the Object properties, and collect the dependencies when the get is done (dep.depend()). Notifies the responding component of updates on set.
letchildOb = ! shallow && observe(val)// Get a child observer (if the current val is an object) Object.defineProperty(obj, key, { enumerable: true.configurable: true.get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) { // The current dependency exists dep.depend() // Add the current observer to the current dep.target if (childOb) { // Do the same for subobjects and subarrays // If the value of this object is an object, we recursively observed this object, so the child object must also have an __ob__ childOb.dep.depend() if (Array.isArray(value)) { dependArray(value) } } } return value }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val // Get the current value /* eslint-disable no-self-compare */ if(newVal === value || (newVal ! == newVal && value ! == value)) {return } /* eslint-enable no-self-compare */ if(process.env.NODE_ENV ! = ='production' && customSetter) { customSetter() } // #7981: for accessor properties without setter if(getter && ! setter)return if (setter) { setter.call(obj, newVal) } else{ val = newVal } childOb = ! shallow && observe(newVal)// Re-observe the new value dep.notify() // Update notification}})Copy the code
The question about dep. target will be left as a hole to fill later
- Non-objects or values are
-
Back in the state.js file, after validateProp is called, we call defineReactive again for the key in the props
In contrast to observe(value) in validateProp, this function responds to the value of props
-
proxy(vm, _props, key)
This function assigns the props key to the _props property of the vue instance