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 frominitPropsFunction 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

  1. 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

    validatePropThe 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 *propThe value is missing and not in the prop configurationdefault = >valueA value offalse*
    • If obtainedvalueThe value is an empty string or withkeyAnd with the samepropNot in the expectation typestringorbooleanHigher 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
  2. Let’s talk about thatobseverfunction

    The contents of the observe function are simple, making several judgments about the value passed in:

    1. Non-objects or values areVNodeType ==> No processing is performed
    2. Values areobserveA = = > to returnvalue.__ob__
    3. Non-server rendering, value array or plain object, extensible, and not vUE instance ==> Create a new oneObserverInstance and return

    About the Observer class:

    1. 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.

    2. 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
    3. The constructor of the Observer class also creates a new Dep object for dependency collection, which I’ll cover later

    4. 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

  3. 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

  4. proxy(vm, _props, key)

    This function assigns the props key to the _props property of the vue instance