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 ofsrc/core/observer/index.jsIn the fileshouldObserveThe global variable is set to zerofalse.
    • This makes thedefineReactiveIn the callobserveIs an invalid call.
    • Because for the objectpropValue of the child componentpropThe value always points to the parent componentpropValue, as long as the parent componentpropIf the value changes, it triggers the re-rendering of the child component, so thisobserveThe process can be omitted.
  • Iterating over the definitionpropsConfiguration. The traversal process does two things:
    • One is to calldefineReactiveThe method is to take eachpropThe corresponding value becomes responsive and can be passedvm._props.xxxAccess to definitionspropsProperty corresponding to.
    • The other is throughproxyPut onvm.xxxAccess proxy tovm._props.xxxOn. One detail here is that for children of non-root instances, the proxy occurs onVue.extendPhases, which will be covered in a later chapter.
  • 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:

  • traversedataObject, throughproxyvm.xxxThe agent tovm._data.xxx
  • callobserveMethods Observe the wholedataThe change of thedataIt also becomes reactive, and it goes throughvm._data.xxxAccess to definitionsdataProperty 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
  • valueThere’s an extra one on the object__ob__Property to point toObserverInstance.
  • At the same timeenumerableThe default setting forfalse, so that whenforThis 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.

    getfunction reactiveGetter ({

      // ...

      return value

    },

    setfunction 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