SoFunction
Updated on 2025-04-12

Learn about the process of Vue instance mount in one article

What exactly did new Vue() do in the process?

function Vue (options) {
  if (.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

vue build function call_init method, but I found that this method is not in this file, but you can see carefully that many initialization methods are defined below the file.

initMixin(Vue);     // Definition _initstateMixin(Vue);    // Define $set $get $delete $watch etc.eventsMixin(Vue);   // Define event $on $once $off $emitlifecycleMixin(Vue);// Define _update $forceUpdate $destroyrenderMixin(Vue);   // definition _render Return to virtualdom

First, you can look at the initMixin method and find that this method defines the _init method on the Vue prototype

._init = function (options?: Object) {
    const vm: Component = this
    vm._uid = uid++
    let startTag, endTag
    if (.NODE_ENV !== 'production' &&  && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }
    vm._isVue = true
    // Merge attributes to determine whether the initialized component is a component. Here, the merge is mainly a mixins or extends method    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else { // Merge vue attributes      vm.$options = mergeOptions(
        resolveConstructorOptions(),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (.NODE_ENV !== 'production') {
      // Initialize proxy interceptor      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    // Initialize component life cycle flag bits    initLifecycle(vm)
    // Initialize component event listening    initEvents(vm)
    // Initialize rendering method    initRender(vm)
    callHook(vm, 'beforeCreate')
    // Initialize the dependency injection content, before initializing data and props    initInjections(vm) // resolve injections before data/props
    // Initialize props/data/method/watch/methods    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')
    /* istanbul ignore if */
    if (.NODE_ENV !== 'production' &&  && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }
    // Mount elements    if (vm.$) {
      vm.$mount(vm.$)
    }
  }

In init(), the beforeCreate lifecycle function will be executed through callHook(vm, beforeCreate) and then the props, methods, and data will be initialized through initState(vm). Then the created lifecycle function will be executed through callHook(vm, 'created') and finally the element will be mounted through vm.$mount(vm.$)

so:
1. Props and data cannot be accessed in the beforeCreate lifecycle function because they have not been initialized yet
2. Similarly, props can be accessed in created functions, methods and data data are also the earliest generation that can call interfaces.
Lifecycle function, but the dom is not mounted at this time, so the dom element cannot be accessed
3. In mounted, the dom has been mounted successfully. Therefore, the dom element can be accessed and the earliest life cycle function that can operate the dom element.

InitState(vm)

export function initState (vm: Component) {
  // Initialize the watcher list of components  vm._watchers = []
  const opts = vm.$options
  // Initialize props  if () initProps(vm, )
  // Initialize the methods method  if () initMethods(vm, )
  if () {
    // Initialize data    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if () initComputed(vm, )
  if ( &&  !== nativeWatch) {
    initWatch(vm, )
  }
}

In initState, the relevant data will be initialized through initProps, initMethods, and initData. Here, the watcher list of the component will be initialized.

Check out initData

function initData (vm: Component) {
  let data = vm.$
  // Get the data on the component  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    .NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      '/v2/guide/#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  const keys = (data)
  const props = vm.$
  const methods = vm.$
  let i = 
  while (i--) {
    const key = keys[i]
    if (.NODE_ENV !== 'production') {
      // The attribute name cannot be duplicated with the method name      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    // The attribute name cannot be repeated with the state name    if (props && hasOwn(props, key)) {
      .NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) { // Verify the legitimacy of the key value      // Mount the data in _data to the component vm, so that you can access the data on the component      proxy(vm, `_data`, key)
    }
  }
  // observe data
  // Responsive listening data is a change in data  observe(data, true /* asRootData */)
}

It should be noted that when initializing data data, the variable names and data in props will be checked that they cannot be repeated. Here, all attributes of data will be hijacked through observe. If you listen to data changes, you will notify the subscriber to update the data to achieve two-way binding of data. This is the data responsive publish subscriber mode that vue implements data responsive publish subscriber mode.Dig a hole to implement this model yourself

Let's look at the mount method: call vm.$mount

.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  // Get or query elements  el = el && query(el)
  /* istanbul ignore if */
  // vue does not allow direct mount to body or page document  if (el ===  || el === ) {
    .NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }
  const options = this.$options
  // resolve template/el and convert to render function
  if (!) {
    let template = 
    // The template template exists, parse the vue template file    if (template) {
      if (typeof template === 'string') {
        if ((0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if (.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${}`,
              this
            )
          }
        }
      } else if () {
        template = 
      } else {
        if (.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {
      // Get element content through selector      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if (.NODE_ENV !== 'production' &&  && mark) {
        mark('compile')
      }
      /**
        * 1. Parse template ast tree
        * 2. Convert ast tree to render syntax string
        * 3. Generate render method
        */
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: .NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: ,
        comments: 
      }, this)
       = render
       = staticRenderFns

      /* istanbul ignore if */
      if (.NODE_ENV !== 'production' &&  && mark) {
        mark('compile end')
        measure(`vue ${this._name} compile`, 'compile', 'compile end')
      }
    }
  }
  return (this, el, hydrating)
}

When calling the vm.$mount method, the template will be parsed into an abstract syntax tree (ast tree) and then the abstract syntax tree will be converted into a render syntax string. Finally, the render method will be generated. After mounting it on the vm, the mount method will be called again.

.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  // Rendering component  return mountComponent(this, el, hydrating)
}

Call mountComponent to render component

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  // If the parsed render function is not obtained, a warning will be thrown  // render is generated by parsing the template file  if (!vm.$) {
    vm.$ = createEmptyVNode
    if (.NODE_ENV !== 'production') {
      /* istanbul ignore if */
      if ((vm.$ && vm.$(0) !== '#') ||
        vm.$ || 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 {
        // The template file for vue was not obtained        warn(
          'Failed to mount component: template or render function not defined.',
          vm
        )
      }
    }
  }
  // Execute the beforeMount hook  callHook(vm, 'beforeMount')

  let updateComponent
  /* istanbul ignore if */
  if (.NODE_ENV !== 'production' &&  && 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 {
    // Define update function    updateComponent = () => {
      // The actual call is defined in _update and renderMixin defined in lifeCycleMixin      vm._update(vm._render(), hydrating)
    }
  }
  // Listen to the current component status and update the component when there is a change in data  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        // Component update caused by data update        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}

At this time, the beforeMount hook function will be triggered, the method of updateComponent to render the page view, and the component data will be listened to. Once a change occurs, the beforeUpdate life hook will be triggered. Finally, the callHook(vm, 'mounted') hook function will be executed. Complete the mount. The updateComponent method mainly executes the render and update methods declared during vue initialization.

The function of render is mainly to generate vnode

// Define the render method on the vue prototype._render = function (): VNode {
    const vm: Component = this
    // The render function comes from the component's option    const { render, _parentVnode } = vm.$options

    if (_parentVnode) {
        vm.$scopedSlots = normalizeScopedSlots(
            _parentVnode.,
            vm.$slots,
            vm.$scopedSlots
        )
    }
    vm.$vnode = _parentVnode
    // render self
    let vnode
    try {
        currentRenderingInstance = vm
        // Call the render method, your own unique render method, pass the createElement parameter, and generate vNode        vnode = (vm._renderProxy, vm.$createElement)
    } catch (e) {
        handleError(e, vm, `render`)
        if (.NODE_ENV !== 'production' && vm.$) {
            try {
                vnode = vm.$(vm._renderProxy, vm.$createElement, e)
            } catch (e) {
                handleError(e, vm, `renderError`)
                vnode = vm._vnode
            }
        } else {
            vnode = vm._vnode
        }
    } finally {
        currentRenderingInstance = null
    }
    // if the returned array contains only a single node, allow it
    if ((vnode) &&  === 1) {
        vnode = vnode[0]
    }
    // return empty vnode in case the render function errored out
    if (!(vnode instanceof VNode)) {
        if (.NODE_ENV !== 'production' && (vnode)) {
            warn(
                'Multiple root nodes returned from render function. Render function ' +
                'should return a single root node.',
                vm
            )
        }
        vnode = createEmptyVNode()
    }
    // set parent
     = _parentVnode
    return vnode
}

_update's main function is to call patch, convert vnode to real DOM, and update to the page

._update = function (vnode: VNode, hydrating?: boolean) {
    const vm: Component = this
    const prevEl = vm.$el
    const prevVnode = vm._vnode
    // Set the scope of the currently activated    const restoreActiveInstance = setActiveInstance(vm)
    vm._vnode = vnode
    // .__patch__ is injected in entry points
    // based on the rendering backend used.
    if (!prevVnode) {
      // initial render
      // Execute specific mount logic      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
    restoreActiveInstance()
    // update __vue__ reference
    if (prevEl) {
      prevEl.__vue__ = null
    }
    if (vm.$el) {
      vm.$el.__vue__ = vm
    }
    // if parent is an HOC, update its $el as well
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
      vm.$parent.$el = vm.$el
    }
    // updated hook is called by the scheduler to ensure that children are
    // updated in a parent's updated hook.
  }

Summarize

This is the end of this article about the Vue instance mount process. For more related Vue instance mount process, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!