What exactly does Vue do when it initializes

Create an instance

The SRC /instance/index.js file is the entry file for creating Vue instances

There are some mixed methods that initialize the methods and properties of the instance before creation

InitMixin (Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue

InitMixin (Vue) initializes mixin

The initMixin method is used to blend in with Vue’s _init method, and the following methods are at the heart of the _init method

  • Merging options Configuration
  • initLifecycle(vm)
  • initEvents(vm)
  • initRender(vm)
  • callHook(vm, ‘beforeCreate’)
  • initInjections(vm) // resolve injections before data/props
  • initState(vm)
  • initProvide(vm) // resolve provide after data/props
  • callHook(vm, ‘created’)

0. Merge the Options configuration

  vm.$options = mergeOptions(  / / merge options
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
  )
Copy the code

Merge options and mount a $options attribute on the instance. Merged what? There are two cases:

  • Initialize new Vue

    When the new Vue constructor is executed, the argument is an object, that is, a user’s custom configuration; Will compare it to the stereotype methods, global API properties defined earlier in VUE; As well as the global vue. mixin parameters, merge these into a new options, and finally assign a new property $options.

  • Initialize the child component

    If the child component is initialized, the parent component parameters are merged, such as events and props defined by the parent component on the child component.

This.$options.data accesses the user-defined data function. This.$options.name accesses the user-defined component name.

1. initLifecycle(vm)

The main function is to confirm the parent relationship of the component and initialize some instance properties.

export function initLifecycle(vm: Component) {
  const options = vm.$options  // Previously merged attributes
  
  let parent = options.parent;
  if(parent && ! options.abstract) {// Find the first non-abstract parent component
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }
  
  vm.$parent = parent  // Find it and assign it
  vm.$root = parent ? parent.$root : vm  // Make the $root property of each child the root component
  
  vm.$children = []
  vm.$refs = {}
  
  vm._watcher = null. vm._isDestroyed =false
  vm._isBeingDestroyed = false
}
Copy the code

2. initEvents(vm)

Initializes listening for custom component events, adding parent listener events to the instance if any exist. The main function is to add custom events registered by the parent component using V-ON or @ to the event center of the child component.

export function initEvents (vm: Component) {
  vm._events = Object.create(null)  // Event center
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners // Merge options
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}
Copy the code

3. initRender(vm)

Initialize slots, render functions, and so on for render rendering. Two things, really. $createElm (h); $createElm (h)

export function initRender(vm) {
  vm._vnode = null. vm._c =(a, b, c, d) = > createElement(vm, a, b, c, d, false)  // Convert to compiler
  vm.$createElement = (a, b, c, d) = > createElement(vm, a, b, c, d, true)  // Convert handwritten. }Copy the code

4. callHook(vm, ‘beforeCreate’)

Call the beforeCreate hook function. For now, all you need to know is that it will execute user-defined lifecycle methods, as well as mixins.

Can the beforeCreate hook use this to access variables defined in data?


5. initInjections(vm)

Inject => state => provide Initialize inject before props/data, so that the contents of inject can be used in props/data, and in Pro

export function initInjections (vm: Component) {
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    toggleObserving(false)  // intentionally not being responsive
    Object.keys(result).forEach(key= >{... defineReactive(vm, key, result[key]) }) toggleObserving(true)}}Copy the code

The main function of resolveInject is to find the current injected data layer by layer from bottom to top

export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    // First define a result to return the result found.
    const result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)
    // The outer for loop iterates over every element of Inject
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // #6574 in case the inject object is observed...
      if (key === '__ob__') continue
      const provideKey = inject[key].from
      let source = vm
      // Then the inner layer uses the while loop to find whether the parent of the inject item provides corresponding dependencies from the bottom up.
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      if(! source) {if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if(process.env.NODE_ENV ! = ='production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}
Copy the code

6. initState(vm)

Initialize the state to be used. The state includes props, Methods, data, computed, and watch. (Here look at props, methods, data)

export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  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

6.1 initProps

  • The main function is to check whether the values accepted by the child components conform to the rule and make the corresponding values directly accessible with this
function initProps(vm: Component, propsOptions: Object) {  // The second argument is the validation rule
  const propsData = vm.$options.propsData || {}  // Props Specifies the value passed by the parent component
  const props = vm._props = {}  // The props component can use this to access the props passed by
  constisRoot = ! vm.$parent// Is the root node
  if(! isRoot) {// Close the response if it is not the root node
    toggleObserving(false)}for (const key in propsOptions) {
    const value = validateProp(key, propsOptions, propsData, vm)
    defineReactive(props, key, value)
    if(! (keyin vm)) {
      proxy(vm, `_props`, key)  // agent this.xx actually calls this._props. Xx
    }
  }
  toggleObserving(true)}Copy the code

6.2 initMethods

  • The main function is to mount methods within methods under this.
function initMethods (vm: Component, methods: Object) {
  const props = vm.$options.props
  for (const key in methods) {
    if(methods[key] == null) {  / / the methods [key] = = = null | | the methods [key] = = = undefined shorthand
      warn('only defines key and no corresponding value)}if(props && hasOwn(props, key)) {
      warn(The method name is the same as the key of the props)}if((key in vm) && isReserved(key)) {
      warn(The method name already exists and begins with an _ or $)
    }
    vm[key] = typeofmethods[key] ! = ='function' ? noop : bind(methods[key], vm)   // methods[key].bind(vm)}}Copy the code

6.3 initData

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if(! isPlainObject(data)) { data = {} }// proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (methods && hasOwn(methods, key)) {
       warn('is the same name as methods)}if (props && hasOwn(props, key)) {
      warn('is the same name as key in props)}else if(! isReserved(key)) {// Key cannot start with an _ or $
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)}Copy the code

initProvide(vm)

It is easy to see that the provide option passed in by the user is obtained first, or if it is a method, it is executed and then mounted to the _provided property of the instance, or if it is not, it is directly mounted to the _provided property of the instance

export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}
Copy the code

7.callHook(vm, ‘created’)

Call the created hook function

Summary: What does Vue initialization do?

1. Merge options. Process component configuration by merging the options passed in with the options of the constructor itself (merge user options with the default options).

2. Initialize vue instance lifecycle attributes, such as root, root, root, parent, children, children, children, refs

3. Initialize listening for custom component events. If there is a parent listening event, add it to the instance

4, Initialize the slots, render functions, etc. There are only two things: slot processing and the declaration of $createElm, the h declaration in the render function

5. Call the beforeCreate hook function to show the initialization of a component before and after creation

6. Initialize injected data, inject data first. As a component, it needs to inject data that has been passed down from its ancestors before it can provide data to its descendants

7, to the props, the methods, data, computed, watch is initialized, including reactive processing

Initialize provide after infusing data passed down from ancestors

Create hook (); create hook ()

10. Mount to the corresponding DOM element. If the component constructor sets the EL option, it will be mounted automatically, so there is no need to manually call $mount to mount it

Reference article: Vue principle analysis