Initialization Process

new Vue

When we use Vue, the first page is new Vue(…). ; In the previous chapter, we found an entry file SRC /platforms/web/entry-runtime-with-compiler.js, which we use step by step to find the Vue constructor definition:

// src/platforms/web/entry-runtime-with-compiler.js
// ...
import Vue from './runtime/index'
// ...
Copy the code
// src/platforms/web/runtime/index.js
import Vue from 'core/index'
// ...
Copy the code
// src/core/index.js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
// Initialize the global API
initGlobalAPI(Vue)
// ...
Copy the code
// src/core/instance/index.js
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '.. /util/index'
// Vue constructor
function Vue (options{
  if(process.env.NODE_ENV ! = ='production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')}// Calls vue. prototype_init, which is defined in initMixin
  this._init(options)
}
// Define the vue.prototype_init method
initMixin(Vue)
/** *  * Vue.prototype.$data * Vue.prototype.$props * Vue.prototype.$set * Vue.prototype.$delete * Vue.prototype.$watch */
stateMixin(Vue)
Prototype.$once * vue.prototype.$off * vue.prototype.$emit */
eventsMixin(Vue)
Prototype.$forceUpdate * vue.prototype.$destroy */
lifecycleMixin(Vue)
Prototype.$nextTick * vue.prototype. _render */
renderMixin(Vue)
export default Vue
Copy the code

_init

// src/core/instance/init.js
export function initMixin (Vue: Class<Component>{
  Vue.prototype._init = function (options? :Object{
    const vm: Component = this
    // Each instance holds a _uid
    vm._uid = uid++
    let startTag, endTag
    /* istanbul ignore if */
    if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }
    // a flag to avoid this being observed
    vm._isVue = true
    // Process component configuration items
    if (options && options._isComponent) {
      // When each child component is initialized, this is where only some performance optimizations have been made
      // Put some of the deeper properties on the component configuration object in the vm.$options option to improve code execution
      initInternalComponent(vm, options)
    } else {
      // Merge options, merge default options and custom options
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    // Set the proxy to delegate attributes on the VM instance to vm._renderProxy
    /* istanbul ignore else */
    if(process.env.NODE_ENV ! = ='production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    $parent, $children, $refs, $root, etc
    initLifecycle(vm)
    // Initializes custom events to handle events and callbacks passed by the parent component
    initEvents(vm)
    $slot = vm.$slot = _render = 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 responsively, and proxy each key to the VM instance
    initInjections(vm)
    // The data responsive core handles props, methods, data, computed, and watch
    initState(vm)
    // Parse 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')
    /* istanbul ignore if */
    if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}
Copy the code

The above code clearly shows what the initialization does. At the end of the initialization, if there is an EL attribute, the vm.$mount is automatically called to mount it, otherwise we need to manually call $mount. And then we’re in the mount phase.

Mount the Vue instance

$mount

Entry file SRC /platforms/web/entry-runtime-with-compiler.js:

/* @flow */
import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'
import Vue from './runtime/index'
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'
const idToTemplate = cached(id= > {
  const el = query(id)
  return el && el.innerHTML
})
/** * the compiler entry * precompiles the template to render function */
// Cache the method on the prototype
const mount = Vue.prototype.$mount
// Redefine the method
Vue.prototype.$mount = function (el? : string | Element, hydrating? : boolean) :Component {
  el = el && query(el)
  // Cannot be mounted on root nodes such as body or HTML
  if (el === document.body || el === document.documentElement) { process.env.NODE_ENV ! = ='production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }
  const options = this.$options
  /** */ render > template > el */ render > template > el */
  if(! options.render) {let template = options.template
    // template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) = = =The '#') {
          // {template: '#app'}, use the node whose id is' app' as the mount node
          template = idToTemplate(template)
          /* istanbul ignore if */
          if(process.env.NODE_ENV ! = ='production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`.this)}}}else if (template.nodeType) {
        // Template is a normal element; get its innerHtml as the template
        template = template.innerHTML
      } else {
        if(process.env.NODE_ENV ! = ='production') {
          warn('invalid template option:' + template, this)}return this}}else if (el) {
      // el
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
        mark('compile')}// Compile the template to get the dynamic rendering function and static rendering function
      const { render, staticRenderFns } = compileToFunctions(template, {
        // In non-production environments, compile-time records the index of the starting and ending positions of the tag attributes in the template string
        outputSourceRange: process.env.NODE_ENV ! = ='production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        // Delimiter, default {{}}
        delimiters: options.delimiters,
        // Whether to keep comments
        comments: options.comments
      }, this)
      // Put two rendering functions on this.$options
      options.render = render
      options.staticRenderFns = staticRenderFns
      /* istanbul ignore if */
      if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
        mark('compile end')
        measure(`vue The ${this._name} compile`.'compile'.'compile end')}}}// Call the method mount on the prototype
  return mount.call(this, el, hydrating)
}
/** * Get outerHTML of elements, taking care * of SVG elements in IE as well. */
function getOuterHTML (el: Element) :string {
  if (el.outerHTML) {
    return el.outerHTML
  } else {
    const container = document.createElement('div')
    container.appendChild(el.cloneNode(true))
    return container.innerHTML
  }
}
Vue.compile = compileToFunctions
export default Vue
Copy the code

As you can see from the above code, the ultimate goal is to get the Render function, whether you define the Render method or the EL and template attributes. Then save on options.

Compile the template and get the render function called to compileToFunctions, which we’ll look at at compileToFunctions.

Last call $mount on the prototype, defined in SRC/platform/web/runtime/index. Js

Vue.prototype.$mount = function (el? : string | Element, hydrating? : boolean) :Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
Copy the code

Actual call mountComponent, defined in SRC/core/instance/lifecycle. Js

mountComponent

// src/core/instance/lifecycle.js
export function mountComponent (vm: Component, el: ? Element, hydrating? : boolean) :Component {
  vm.$el = el
  if(! vm.$options.render) { vm.$options.render = createEmptyVNodeif(process.env.NODE_ENV ! = ='production') {
      /* istanbul ignore if */
      if ((vm.$options.template && vm.$options.template.charAt(0)! = =The '#') ||
        vm.$options.el || el) {
        warn(
          'You are using the runtime-only build of Vue where the template ' +
          'compiler is not available. Either pre-compile the templates into ' +
          'render functions, or use the compiler-included build.',
          vm
        )
      } else {
        warn(
          'Failed to mount component: template or render function not defined.',
          vm
        )
      }
    }
  }
  callHook(vm, 'beforeMount')
  let updateComponent
  /* istanbul ignore if */
  if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
    updateComponent = () = > {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`
      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)
      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    // Execute vm._render() to get the virtual DOM and pass vNode to _update
    updateComponent = () = > {
      vm._update(vm._render(), hydrating)
    }
  }
  // we set this to vm._watcher inside the watcher's constructor
  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  // component's mounted hook), which relies on vm._watcher being already defined
  new Watcher(vm, updateComponent, noop, {
    before () {
      if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true /* isRenderWatcher */)
  hydrating = false
 
 $vnode represents the parent virtual Node of the Vue instance, so if it is Null, it is the instance of the root Vue
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')}return vm
}
Copy the code

The mountComponent defines the updateComponent method, instantiates a Watcher, and passes updateComponent as an argument to be called in the Watcher callback. Watcher here mainly performs callback functions for initialization and data changes.

_isMounted = true indicates that the instance is mounted.

The updateComponent call executes vm._update and vm._render. Vm. _render gets the virtual DOM,vm._update updates the view.

BeforeMount, beforeUpdate, and Mounted are three lifecycle hooks. That is, before vm._render(), the beforeMount hook function is executed; After vm._update() converts the virtual DOM to the real DOM, execute the Mounted hook function. If _isMounted is used to indicate mounting when data changes, run the beforeUpdate hook function.

Mounted hook is executed only if the parent virtual Node is null. Only the new Vue goes there, if it’s a component, and its parent virtual Node exists. Component mounted elsewhere.

A link to the

Vue (V2.6.14) source code detoxification (pre) : handwritten a simple version of Vue

Vue (V2.6.14) source code detoxification (a) : preparation

Vue (V2.6.14) source code detoxification (two) : initialization and mount

Vue (V2.6.14) source code detoxification (three) : responsive principle (to be continued)

Vue (V2.6.14) source code detoxification (four) : update strategy (to be continued)

Vue (v2.6.14) source code detoid (v) : Render and VNode (to be continued)

Vue (v2.6.14) source code: Update and patch (to be continued)

Vue (v2.6.14) source code detoxification (seven) : template compilation (to be continued)

If you think it’s ok, give it a thumbs up!! You can also visit my personal blog at www.mingme.net/