The target

Learn more about the Vue initialization process and don’t be afraid to ask the interviewer what happened to new Vue(options).

To find the entrance

To find out what new Vue(options) does, you must first find where the Vue constructor is declared. There are two ways to do this:

  • Finding the entry to compile from the rollup configuration file and then working your way through the Vue constructor is a laborious process

  • By writing sample code and then breaking points, it’s easy to get there in one step

Let’s do the second way, write examples, break points, and do it all at once.

  • in/examplesAdd an example file — to the directorytest.html, add the following content to the file:
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Vue source interpretation</title>
</head>
<body>
  <div id="app">
    {{ msg }}
  </div>
  <script src=".. /dist/vue.js"></script>
  <script>
    debugger
    new Vue({
      el: '#app'.data: {
        msg: 'hello vue'}})</script>
</body>
</html>
Copy the code
  • Open the console in your browser, and then opentest.html, you go to breakpoint debugging, and then find the file where the Vue constructor is

Get Vue constructor in/SRC/core/instance/index. The js file, then read the official start of the source code, with the goal to read.

Encountered in the process of reading such as see don’t understand, but by writing code, and then use the browser’s debugging to debug step by step, with understanding, if you still don’t understand, just make a note to continue to look back, you may see other places, suddenly understand what this place is doing, or look back, will understand, this matter source, Be sure to read more, to master, once or twice is certainly not enough

Source code interpretation — Vue initialization process

Vue

/src/core/instance/index.js

import { initMixin } from './init'

// The Vue constructor
function Vue (options) {
  // Call the vue.prototype. _init method, which is defined in initMixin
  this._init(options)
}

// Define the vue.prototype. _init method
initMixin(Vue)

export default Vue
Copy the code

Vue.prototype._init

/src/core/instance/init.js

/** * Define the vue.prototype. _init method@param {*} Vue Vue constructor */
export function initMixin (Vue: Class<Component>) {
  // Responsible for the Vue initialization process
  Vue.prototype._init = function (options? :Object) {
    / / the vue instance
    const vm: Component = this
    // Each vue instance has a _UID and is incrementing successively
    vm._uid = uid++

    // a flag to avoid this being observed
    vm._isVue = true
    // Handle component configuration items
    if (options && options._isComponent) {
      /** * Go here for initialization of each subcomponent, but there are only some performance optimizations ** put some deep properties on the component configuration object into the VM.$options option to improve code execution */
      initInternalComponent(vm, options)
    } else {
      /** * When initializing the root component, go here and merge Vue's global configuration into the root component's local configuration. For example, Vue.component registered global components are merged into the root instance's Components option. * 1, global components registered by the Vue.component method are registered with an option merge * 2, {components: Partial components registered in {xx}} mode execute the compiler generated render function with options merged, including components configuration */ in the root component
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if(process.env.NODE_ENV ! = ='production') {
      // Set the proxy to proxy properties on the VM instance to vm._renderProxy
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    $parent, $children, $root, $refs, and so on
    initLifecycle(vm)
    /** * initialize the custom event, a bit of attention here, so we in <comp@click="handleClick" /> < p style =" padding-bottom: 0px; padding-bottom: 0px; padding-bottom: 0px; padding-bottom: 0px
    initEvents(vm)
    $slot = vm.$slot = vm.$createElement = h
    initRender(vm)
    // Call the beforeCreate hook function
    callHook(vm, 'beforeCreate')
    // Initialize the inject configuration item of the component, get the configuration object in the form of result[key] = val, and then process the result data in response, and proxy each key to the VM instance
    initInjections(vm) // resolve injections before data/props
    // Functions as props, methods, data, computed, and watch
    initState(vm)
    // Resolve the provide object on the component configuration item and mount it to the VM. _provided property
    initProvide(vm) // resolve provide after data/props
    // Call the created hook function
    callHook(vm, 'created')

    // If the configuration item has the EL option, then the $mount method is automatically called. If the el option is available, then the $mount method is automatically called
    if (vm.$options.el) {
      // Call the $mount method to enter the mount phase
      vm.$mount(vm.$options.el)
    }
  }
}

Copy the code

resolveConstructorOptions

/src/core/instance/init.js

/** * resolve the configuration object options from the component constructor and incorporate the base class options *@param {*} Ctor 
 * @returns * /
export function resolveConstructorOptions (Ctor: Class<Component>) {
  // Configure the project
  let options = Ctor.options
  if (Ctor.super) {
    // There is a base class, and the option to recursively resolve the base class constructor
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions
    if(superOptions ! == cachedSuperOptions) {// The base class constructor option has changed and needs to be reset
      Ctor.superOptions = superOptions
      // Check to see if there are any late changes/additional options on ctor. options (# 4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // Merge the two options if they have been modified or added
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      // Merge options, and assign the result to ctor.options
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}

Copy the code

resolveModifiedOptions

/src/core/instance/init.js

/** * Parse constructor options that are subsequently modified or added */
function resolveModifiedOptions (Ctor: Class<Component>): ?Object {
  let modified
  // Constructor options
  const latest = Ctor.options
  // Sealed constructor option, backup
  const sealed = Ctor.sealedOptions
  // Compare the two options and record the ones that do not agree
  for (const key in latest) {
    if(latest[key] ! == sealed[key]) {if(! modified) modified = {} modified[key] = latest[key] } }return modified
}

Copy the code

mergeOptions

/src/core/util/options.js

/** * Merge two options. When the same configuration item appears, the child option overwrites the configuration of the parent option */
export function mergeOptions (
  parent: Object,
  child: Object, vm? : Component) :Object {
  if(process.env.NODE_ENV ! = ='production') {
    checkComponents(child)
  }

  if (typeof child === 'function') {
    child = child.options
  }

  // standardise props, inject, and directive options
  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)

  // Handles extends and mixins on the original Child object, executing mergeOptions, respectively, to merge these inherited options into the parent
  // mergeOptions handles objects that have the _base attribute
  if(! child._base) {if (child.extends) {
      parent = mergeOptions(parent, child.extends, vm)
    }
    if (child.mixins) {
      for (let i = 0, l = child.mixins.length; i < l; i++) {
        parent = mergeOptions(parent, child.mixins[i], vm)
      }
    }
  }

  const options = {}
  let key
  // Iterate over the parent option
  for (key in parent) {
    mergeField(key)
  }

  If the parent option does not have the configuration, merge it, otherwise skip it, because the case of the parent having the same property was already handled when the parent option was processed above, using the value of the child option
  for (key in child) {
    if(! hasOwn(parent, key)) { mergeField(key) } }// Merge options, childVal takes precedence over parentVal
  function mergeField (key) {
    // strats = Object.create(null)
    const strat = strats[key] || defaultStrat
    // If childVal exists, childVal is preferred; otherwise, parentVal is used
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

Copy the code

initInjections

/src/core/instance/inject.js

/** * Initializes the inject configuration item * 1, and gets result[key] = val * 2, and processes the result data in a responsive way, and proxy each key to the VM instance */
export function initInjections (vm: Component) {
  // Parse the inject configuration item, then find the val corresponding to each key in the configuration item from the parent component configuration, and finally get result[key] = val
  const result = resolveInject(vm.$options.inject, vm)
  // Do data response processing on result, also have the function of each key in the proxy inject configuration to the VM instance.
  // It is not recommended to change the data in the child component, because if the provide injected in the parent component changes, the changes you made in the component will be overwritten
  if (result) {
    toggleObserving(false)
    Object.keys(result).forEach(key= > {
      /* istanbul ignore else */
      if(process.env.NODE_ENV ! = ='production') {
        defineReactive(vm, key, result[key], () = > {
          warn(
            `Avoid mutating an injected value directly since the changes will be ` +
            `overwritten whenever the provided component re-renders. ` +
            `injection being mutated: "${key}"`,
            vm
          )
        })
      } else {
        defineReactive(vm, key, result[key])
      }
    })
    toggleObserving(true)}}Copy the code

resolveInject

/src/core/instance/inject.js

/** * parse the inject configuration item and find the value of key from the provide component, otherwise use the default value, result[key] = val * inject Because the component configuration object is normalized when merging options *@param {*} inject = {
 *  key: {
 *    from: provideKey,
 *    default: xx
 *  }
 * }
 */
export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    const result = Object.create(null)
    // inject all keys of the configuration item
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)

    / / traverse the key
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // Skip the __ob__ object
      // #6574 in case the inject object is observed...
      if (key === '__ob__') continue
      // Get the corresponding key in provide
      const provideKey = inject[key].from
      let source = vm
      // Iterate through all the ancestor components to the root component and find the corresponding key value in provide. Finally result[key] = provide[provideKey]
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      // If the previous loop was not found, inject[key].default is used. If default is not set, an error is thrown
      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

initProvide

/src/core/instance/inject.js

/** * Resolve the provide object on the component configuration item and mount it to the VM. _provided property */
export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

Copy the code

conclusion

What does the initialization process for Vue(new Vue(options)) do?

  • Handle component configuration items

    • When the root component is initialized, an option merge operation is performed to merge the global configuration onto the local configuration of the root component

    • Some performance optimizations were made when each subcomponent was initialized, and some deep properties on the component configuration object were placed in the VM.$options option to improve code execution

  • Initialize the relationship properties of the component instance, such as $parent, $children, $root, $refs, and so on

  • Handle custom events

  • Call the beforeCreate hook function

  • Initialize the inject configuration item of the component, obtain the configuration object in the form of Ret [key] = val, and then perform reactive processing on the configuration object, and proxy each key to the VM instance

  • Data response, such as props, methods, data, computed, and watch

  • Resolve the provide object on the component configuration item and mount it to the VM. _provided property

  • Call the created hook function

  • If the el option is found on the configuration item, the $mount method is automatically called. If the EL option is present, there is no need to call the $mount method manually. Otherwise, the $mount method must be called if the EL option is not provided

  • Next, you enter the mount phase

Form a complete set of video

Vue source code interpretation (2) — Vue initialization process

Please focus on

Welcome everyone to follow my gold mining account and B station, if the content has to help you, welcome everyone to like, collect + attention

link

  • Vue source code interpretation (1) – preface

  • Vue source code interpretation (2) — Vue initialization process

  • Vue source code interpretation (3) – response principle

  • Vue source code interpretation (4) — asynchronous update

  • Vue source code Interpretation (5) — Global API

  • Vue source code interpretation (6) — instance method

  • (7) — Hook Event

  • Vue source code interpretation (8) — compiler parsing

  • Vue source code interpretation (9) — compiler optimization

  • Vue source code interpretation (10) — compiler generation rendering function

  • (11) — Render helper

  • Vue source code interpretation (12) — patch

Learning exchange group

link