preface

Before we begin:

  • The main purpose of this article is to understand the general principle of Mounted.

  • The Vue version used is 2.6.11, and a simple demo created by VUE-CLI is an example to help you understand

  • Ignore for the moment features including but not limited to slot, functional components, server-side rendering, and other features that don’t affect understanding

  • The code posted is not exactly the same as the source code, there will be some changes and cuts as long as there are no errors (I think this helps to understand)

  • I’m not going to explain the code line by line, and it’s okay to leave out the parts that aren’t mentioned (if it’s really not careless)

Then, the sample code used for this article is as follows:

main.js

import Vue from 'vue'
import App from './App'

new Vue({
  render: h= > h(App)
}).mount('#app')
Copy the code

App.vue

<template>
	<div>
    <div>hello, {{ msg }}</div>
    <home></home>
  </div>
</template>

<script>
import home from './home'

export default {
  name: 'App',
  
  components: { home },
  
  data() {
    return {
      msg: 'app'
    }
  }
}
</script>
Copy the code

home.vue

<template>
	<div>
    <div>hello, {{ msg }}</div>
    <div>
      home component
    </div>
  </div>
</template>

<script>

export default {
  name: 'Home',
  
  data() {
    return {
      msg: 'home'
    }
  }
}
</script>
Copy the code

New Vue({render: H => h(App)}). Mount (‘# App ‘) to the page to display the corresponding DOM content. Vue and the attributes of the VM instance created by it, render of vNode and VM instance, initial patch and VueComponent.

Vue and vm

We know that all *. Vue files are ultimately represented at application runtime as a VM instance that has a common root VM, the former created by New VueComponent, the latter created by New Vue, VueComponent in turn inherits from Vue, so since all view-related content is dependent on Vue and VM, we need to know what properties (and methods) they have and how those properties are assigned:

The constructorVue

/ / 1
function Vue(options) {
  this._init(options)
}

/ / 2
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifeCycleMixin(Vue)
renderMixin(Vue)

/ / 3
initGlobalAPI(VUe)

/ / 4
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
  get: function get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
});

Vue.version = '2.6.11'

// 5 install platform specific utilsVue.config.mustUseProp = mustUseProp Vue.config.isReservedTag = isReservedTag Vue.config.isReservedAttr = isReservedAttr  Vue.config.getTagNamespace = getTagNamespace Vue.config.isUnknownElement = isUnknownElement// 6 install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method
Vue.prototype.$mount = function (el, hydrating) {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
Copy the code

As the name implies, the purpose of those xxxmixins is to add XXX related properties to vue. prototype:

  • InitMixin:Vue.prototypeadd_initMethod, this method is in thenew Vuenew VueComponentWhen executed
  • StateMixin:Vue.prototypeadd$set,$delete,$watchmethods
  • EventMixin:Vue.prototypeadd$on,$once,$emit,$offmethods
  • LifeCycleMixin:Vue.prototypeadd$forceUpdate,$destroy,_updatemethods
  • RenderMixin:Vue.prototypeadd_render,$nextTickAnd other Runtime convenience helpers (these method names are made up of an underscore and a single letter, called by<template>Compile andrenderAll of these methods are used in

InitGlobalAPI adds attributes to Vue:

  • Config and util: not usually used
  • Options: Including components, directives, filters, and _base, where the _base value is generallyVueItself, for inheritance
  • Set, DELETE, Observable, nextTick: These attributes are different from those ofVue.prototypeCorresponding to the related attributes of
  • Use, mixin: Used to add plugins and mixins
  • Extend: VueComponent is generated
  • Component, directive, filter: used forVue.optionsAdd attributes to the corresponding properties, such as registering a component, directive, and filter

After initGlobalApI(Vue) is executed, Vue.options.components only contains builtInComponents (only a KeepAlive), PlatformComponent (Transition and TransitionGroup) was added. Vue. Options. Directive contains both model and show directives.

The __patch__ and $mount methods are two of the most important, the latter used to start mounting an instance, and the former used to generate the DOM by comparing old and new VNodes.

That’s all the stereotype and static attributes we got from import Vue from ‘Vue’.

vm

New Vue({render: h => h(App)}), _init({render: h => h(App)}) :

Vue.prototype._init = function _init(options) {
  const vm = this
  
  vm._uid = $uid++
  vm._isVue = true
  // vuecomponent._init the next line of code will be different
  vm.$options = mergeOptions(Vue.options, options, vm) / / 1
  
  vm._renderProxy = vm
  vm._self = vm
  
  / / 2
  initLifeCycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm)
  initState(vm)
  initProvide(vm)
  callHook(vm, 'created')
  
  if(options.el) {
    vm.$mount(options.el)
  }
}
Copy the code

_uid is the unique identifier of each VM and increases from 0. The root component is 0.

The mergeOptions function merges the first parameter and the attributes contained in the second parameter __ (remember there is such a thing) into a new option stored in the vm.$options property or in the VM itself (such as state), depending on the __ merge strategy When we access those attributes on the VM, we get the final merged value.

Those initxxx are used to add XXX related attributes to the VM:

  • InitLifeCycle:
    • $parent and $root: the initial values of the parent and root components of the current VM are correct
    • $chidren, $refs: the former is used to save the child components mounted under the current VM, initialized as an empty array; The latter saves the refs owned by the current component, initialized to{}
    • _watcher, _inactive: The former is used to save the render- Wacher instance of the current VM (render-watcher is a type of watcher instance, each VM has only one) initialized tonull; The latter is used toKeepAliveComponent, initialized tonull
    • _directInactive, _isMounted, _isDestroyed, _isBeingDestroyed: The first attribute is also usedKeepAliveComponent, initialized tofalse; All other attributes are initialized tofalse
  • InitEvents:
    • _events: Used to hold events bound to the current component, initialized toObject.create(null)
    • _hasHookEvent: Flags whether the event of the current component contains a name similar tohook:createdSuch an event is initialized tofalse
  • InitRender:
    • _vnode: used for savingvm.render()Is initialized tonull
    • $vNode: Called component vNode or placeholder vnode, the current component is in its parent component_vnodeIn the form of the final call itselfrender()Generated aftervm._vnodeIs initialized in its parent component_vnodeRepresents the component VNode value. The root component can be inferred$vnodeValue is empty.
    • _c and $createElement: both used to generate vNode instances, the former used inside the render function compiled from the Template template, and the latter exposed as handwritingrender(h) { return h('div', 'hello') }The first argument to the function, for examplemain.jsIn the{ render: h => h(App) }
    • _staticTrees $slots $Listeners
  • InitInjections: processingvm.$options.inject,vm.kEquivalent accessvm.options.inject.k
  • InitState:
    • Initialize thevm._watchers[]Is used to save all watcher instances created under the current VM, including render-Watcher, lazy-watcher (generated when handling computed data), and general Watcher
    • To deal withvm.$optionsIn props, methods, data, and computed, their attributes can also be used directly invmBe accessed on
    • To deal withvm.$options.watcherCreate watcher instances for each of the things being watched
  • InitProvide: processingvm.$options.provideTo generate thevm._provideIs used as the inject of sub-components

The callHook executes the lifecycle hook in vm.$options and triggers the corresponding hook event if the current vm._hasHookEvent is true.

This is the process of creating a VM, followed by executing vm.$mount($el).

Render of vNode and VM instances

Before explaining what a vnode is and where it comes from, take a look at the $mount code:

Vue.prototype.$mount = function(el) {
  return mountComponent(this, el)
}

function mountComponent(vm, el) {
  vm.$el = el
  
  callHook(vm, 'beforeMount')
  
  const updateComponent = () = > vm._update(vm._render())
  
  new Watcher(vm, updateComponent, () = > {}, {
    before: () = > {
      if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true /* true to render-watcher */)
  
  if(vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')}}Copy the code

You can see that the mountComponent function does this:

  1. forvm.$elThe assignment
  2. callHook(vm, 'beforeMount')
  3. Generate a render- Watcher instance for the current VM: Forget the detailed process and internal mechanics of generating render- Watcher, just remember that it is generated when it is executedupdateComponentFunction, which in turn will be executedvm._render()vm._update(vnode)
  4. checkvm._$vnode == nullIf yes, the call is manually invokedcallHook(vm, 'mounted')As mentioned earlier, there are usually only root componentsvm.$vnodeIs empty, so this fourth step is only for the root component$mountWill walk to)

It is these four steps that render all the components into the DOM. As you can imagine, some action in step 3 triggers the render of the child component, which then triggers the render of the child component in step 3 until all the leaf components are rendered. Here is a brief introduction to VNode.

VNode

A VNode is a simplification of a real node in the DOM. Compared to a node with a bunch of attributes created by document.createElement, a vNode only needs the necessary attributes: tag name, attribute set, child node list, and text value to correspond to a node. In Vue, vnode is created by the constructor vnode:

class VNode {
  constructor(tag, data, children, text, elm, context, componentOptions) {
    this.tag = tag
    // ...
    this.componentOptions = componentOptions
    this.componentInstance = undefined}}Copy the code

These parameters generate a vNode (of course, the vNode instance has a few other attributes, but we’ll focus on those for now) that is called a generic vnode when the tag is empty or corresponds to the tag name in the DOM, otherwise it is called a component vNode. Each component VNode corresponds to a VM. Data is used to store various properties associated with real Nodes: class, style, bound native events, etc. Just like real nodes have child nodes, children are used to store child VNodes; Text corresponds to a text node; Elm points to the node generated by the current VNode; Context refers to the VM instance where the current VNode resides. The componentOptions component is proprietary to vNode, which is used to store some data used to generate child components. ComponentInstance is also a component vNode. When a component VNode is about to be converted to a Node, a VM instance corresponding to the component VNode is generated, and then componentInstance points to the instance.

With a basic understanding of vNodes, let’s take a look at when vNodes are generated and how they are converted into real nodes.

Render of the VM instance

Render – Watcher is created using vm._render(). The _render method was added to the prototype object when renderMixin(Vue) was created. $options.render. Call (vm, vm.$createElement). The effect of this statement is to set vm as this. Vm. $options.render is passed as an argument to vm.$options.render, which is vm.$createElement(App) for the root component.

vm.$createElement = function (a, b, c) {
  return _createElement(vm, a, b, c)
}

function _createElement(context, tag, data, children) {
  // Context is vm, tag is App when the root component is created
  if(! tag) {return createEmptyVNode()
  }
  
  let vnode
  
  if(typeof tag === 'string') {
    let Ctor
    // with the same name as the HTML native tag
    if(config.isReservedTag(tag)) {
      vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined.undefined, context
      )
     // For a custom component, check the Context.code.components.ponents property to see if the component is registered
     // The child component's render takes this step
    } else if(Ctor = resolveAsset(context.$options, 'components', tag))) {
      vnode = createComponent(Ctor, data, context, children, tag)
    }
   // Tag is the corresponding options of the component, such as App, and the render of the root component takes this step
  } else {
    vnode = createComponent(tag, data, context, children)
  }
  
  return vnode || createEmptyVNode()
}
Copy the code

When the tag is HTML native, call new VNode directly to generate a normal VNode return. When tag is a custom component name, the options of the custom component are resolved from the parent VM instance’s $Options.com ponents and then the createComponent is called for it and the return value is assigned to vNode. Other cases treat tag as options for a component directly. New Vue({components: {App}, render: {components: {App}, render: H => h(‘App’)}), so I added the last logic that treats the options corresponding to the component as a tag, saving a dozen characters in the main.js file.

So the next thing we need to look at is createComponent1 (there is also a function called createComponent in the source code, with a number added to avoid confusion) :

function createComponent1(Ctor, data, context, children, tag) {
  if(isUndef(Ctor)) {
    return
  }
  
    // The baseCtor is usually Vue
  var baseCtor = context.$options._base
  
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor);
  }
  
  data = data || {}
  
  var propsData = extractPropsFromVNodeData(data, Ctor, tag)
  var listeners = data.on
  
  data.on = data.nativeOn
  
  // resolve constructor options in case global mixins are applied after
  // component constructor creation
  resolveConstructorOptions(Ctor)
  
  // Add its own hooks for the component vNode
  installComponentHooks(data)
  
  var name = Ctor.options.name || tag
  
  return new VNode(
    ('vue-component-' + (Ctor.cid) + (name ? (The '-' + name) : ' ')),
    data, undefined.undefined.undefined, context,
    { Ctor, propsData, listeners, tag, children }
  )
}
Copy the code

In addition to the first parameter tag name, data is also handled by installComponentHooks. In addition, there is a parameter componentOptions that general VNode does not have. It is these differences that make the patch process of VM, Each component vNode is processed to create the corresponding VueComponent constructor (componentOptions.ctor) from which the corresponding VM instance is generated. The generated VM performs its own _init, _render, _update to generate the next subinstance… Repeat until all components of the VNode are handled as they should be.

Initial patch of a component and generation of sub-components

The patch of a component refers to the process in which the corresponding Vnode of a component is converted into a real node and inserted into its parent node. Because each component instance has a component VNode instance corresponding to one of the components, the patches of the following components and vnodes refer to the same thing.

After receiving the vNode instance returned by vm.render(), the VM instance starts to patch. This process is performed at the instance level by vm._update and vm.__patch__ to convert the vNode into a real node. We know that the general VNode can be directly transformed into a real Vnode by the relevant API in DOM. How is the component VNode handled during the patch process?

The first patch

Vue.prototype._update = function(vnode) {
  const vm = this
  const prevEl= vm.$el
  const restoreActiveInstance = setActiveInstance(vm)
  
  vm._vnode = vnode
  $el is used as oldVnode and is passed to vm.__patch__
  // vm.$el is document.getelementById ('App') when vm is the root instance, null when not the root component
  vm.$el = vm.__patch__(vm.$el, vnode)
  
  restoreActiveInstance()
  
  if (prevEl) {
    prevEl.__vue__ = null;
  }
  if (vm.$el) {
    vm.$el.__vue__ = vm;
  }
}

Vue.prototype.__patch__ = function(oldVnode, vnode) {
  const insertedVnodeQueue = []
  let isInitialPatch = false
  
  if(isUndef(oldVnode)) {
    // 1. The first patch of the non-root component
    isInitialPatch = true
    createElm(vnode, insertedVnodeQueue)
  } else if(isDef(oldVnode.nodeType)) {
    New Vue({render: h => h(App)}).$mount('App')
    
    oldVnode = emptyNodeAt(oldVnode)
    
    const oldElm = oldVnode.elm
    const parentElm = nodeOps.parentNode(oldElm)

    createElm(vnode, insertedVnodeQueue, parentElm, nodeOps.nextSibling(oldElm))
  }
  
  invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
  
  return vnode.elm
}
Copy the code

The first patch function is createElm, and the key code is as follows:

function createElm(vnode, insertedVnodeQueue, parentElm, refElm) {
  // 3. Check whether it is a component VNode. If yes, exit the current function and the component vNode generates the corresponding VueComponent to generate the corresponding VM
  // This is done inside createComponent, which is the aforementioned createComponent2
  if(createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
    return
  }
  
  const data = vnode.data
  const children = vnode.children
  const tag = vnode.tag
  CreateElm is recursively called on each child vNode when createChildren executes
  if(isDef(tag)) {
    // 4. Tag is a valid HTML tag, and createElement is a DOM node that calls document.createElement
    vnode.elm = nodeOps.createElement(tag, vnode)
    createChildren(vnode, children, insertedVnodeQueue)
    
    if (isDef(data)) {
      invokeCreateHooks(vnode, insertedVnodeQueue)
    }
    // Insert inserts the DOM node where it should be, within its parent element
    insert(parentElm, vnode.elm, refElm)
  } else if(isTrue(vnode.isComment)) {
    // 5. Annotate the node, insert directly
    vnode.elm = nodeOps.createComment(vnode.text)
    insert(parentElm, vnode.elm, refElm)
  } else {
    // 6. Text node, direct insert
    vnode.elm = nodeOps.createTextNode(vnode.text)
    insert(parentElm, vnode.elm, refElm)
  }
}
Copy the code

CreateElm handles different types of VNodes as described above. The responsibility for handling the component VNode is done by createComponent2, and it is here that the child component begins its life cycle.

Generation of child components

function createComponent2(vnode, insertedVnodeQueue, parentElm, refElm) {
  let i = vnode.data
  
  if(isDef(i) && isDef(i = i.hook) && isDef(i = i.init)) {
    i(vnode)
  }
  
  if(isDef(vnode.componentInstance)) {
    initComponent(vnode, insertedVnodeQueue)
    insert(parentElm, vnode.elm, refElm)
    
    return true}}Copy the code

We know that the vnode.componentInstance attribute is empty when the vnode instance is created, so it must be assigned during vnode.data.hook.init(vnode). CreateComponent1 has installComponentHooks(Data), which adds proprietary hooks for each component vNode. Which includes init (which contains hooks prepatch, insert, destroy) :

const componentVNodeHooks = {
  init(vnode) {
    const child = vnode.componentInstance = createComponentInstanceForVnode(
      vnode,
      ActiveInstance is the parent component instance of child
      activeInstance
    )
    child.$mount()
  }
  / /... Other hooks
}

function createComponentInstanceForVnode(vnode, parent) {
  const options = {
    _isComponent: true._parentVnode: vnode,
    parent: parent
  }
  
  return new vnode.componentOptions.Ctor(options)
}
Copy the code

The result of the init hook is to actually generate a VM instance for the component vNode and assign it to the vnode.ponentInstance attribute Vnode vnode) or components, and then implement vm. $mount, generate examples of work by vnode.com ponentOptions. Ctor (options), and the Ctor is child component constructor, VueComponent, which was generated in createComponent1 earlier by vue.extend (options) (the options are the objects that each single-file component is processed by vue-loader) :

Vue.exntend = function(extendOptions = {}) {
  const Super = this
  const superId = Super.cid
  const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
  
  if (cachedCtors[superId]) {
    return cachedCtors[superId]
  }
  
  const name = extendOptions.name || Super.options.name
  
  const Sub = function VueComponent(options) {
    this._init(options)
  }
  
  Sub.prototype = Object.create(Super.prototype)
  Sub.prototype.constructor = Sub
  Sub.cid = cid++
  Sub.options = mergeOptions(Super.options, extendOptions)
  Sub['super'] = Super
  
  if (Sub.options.props) {
    initProps$1(Sub)
  }
  if (Sub.options.computed) {
    initComputed$1(Sub)
  }
  
  Sub.extend = Super.extend
  Sub.mixin = Super.mixin
  Sub.use = Super.use
  
  Vue.component. directive, vue.filter
  ASSET_TYPES.forEach(function (type) {
    Sub[type] = Super[type]
  })
  
  if (name) {
    Sub.options.components[name] = Sub
  }
  
  Sub.superOptions = Super.options
  Sub.extendOptions = extendOptions
  Sub.sealedOptions = extend({}, Sub.options)
  
  cachedCtors[SuperId] = Sub
  
  return Sub
}

// the JSON format string corresponding to App options, the two null because the original value is a function
{
  "name": "App"."components": {
    "home": {
      "name": "Home"."staticRenderFns": []."_compiled": true."beforeCreate": [null]."beforeDestroy": [null]."__file": "src/home.vue"}},"staticRenderFns": []."_compiled": true."beforeCreate": []."beforeDestroy": []."__file": "src/App.vue"
}
Copy the code

VueComponent, the component’s constructor, inherits the Vue constructor directly, but does not fully match it in its static properties:

  1. Fewer than the latter: config, util, set, delete, nextTick, Observable, FunctionalRenderContext

  2. More than the latter: super, superOptions, extendOptions, sealedOptions

  3. With changes: options

Instead of the options of new Vue(Options) being used directly to generate the VM instance, the options of the component are used first to generate the corresponding Vuecomponent constructor. The options of the component are passed to vue. extend as extendOptions and vue. options are treated by mergeOptions as a new option assigned to the vuecomponent. options property. This helps reduce duplication because there is only one root component (no need to do this) and the same child component can be instantiated in multiple places, so you can store a VueComponent in extendOptions.ctor when it is first generated, corresponding to its Super CID. This saves a lot of effort each super.extend session as long as Super stays the same. VueComponent inherits VueComponent directly from Vue as long as the options of a component do not explicitly override _base. This means that vuecomponent.options. _Ctor for each child component has only one property with a value of 0 and vuue.

InitComputed $1 and initProps$1 initialize all properties in the computed object of the child component to the getter/setter of VueComponent. Prototype, and proxy all properties in the props to VueComponent. Prototype _props

This is how Vuecomponent is generated. With the constructor, the instantiation of the child component is basically the same as that of the root component, which calls this._init(options) through each lifecycle of the component VM. Also, take a look at what render looks like generated by vue-loader:

ƒ () {
  var _vm = this
  var _h = _vm.$createElement
  var _c = _vm._self._c || _h
  return _c(
    "div",
    [_c("div", [_vm._v("hello, " + _vm._s(_vm.msg))]), _c("home")].1)}Copy the code

RenderMixin/renderMixin/renderMixin/renderMixin/renderMixin/renderMixin

New Vue({render: h => h(App)}).$mount(‘App’) to DOM for all vm instances

  1. The root component VM0 patch is processed byAppGenerated component VNode
  2. An instance of VM1 is generated for this component, VNode. Vm1 is initialized and mounted
  3. Repeat step 2 if you meet other component Vnodes during vm1 patch. Otherwise, it will be treated as a general Vnode (children dealing with general Vnodes may contain component Vnodes).
  4. Repeat step2 and step3 until all vnodes have been converted into DOM nodes and inserted into their rightful positions

/ / Mount a DOM node to its parent DOM node. / / Mount a DOM node to its parent DOM node. / / Mount a DOM node to its parent DOM node. Finally, see how the real node generated by each component vNode is inserted into the parent node, and how the corresponding VM instance triggers the Mounted hook in order.

Mounted hooks are triggered on all components

At the end of the patch method, each component vNode fires invokeInsertHook at the end of the patch:

function invokeInsertHook(vnode, queue, isInitialPatch) {
  if(isTrue(isInitialPatch) && isDef(vnode.parent)) {
    // Vnode. parent is a component vnode. When the VM is first patched, the corresponding component vnode will change the current value
    // The queue for all vNodes whose patches have been completed is stored in data.pendingInsert.
    // The vnode itself will be pushed to its parent vNode.data.pendingInsert during subsequent initComponent phases.
    vnode.parent.data.pendingInsert = queue
  } else {
    // When invokeInsertHook is executed within the patch of the root component, the invokeInsertHook is the component vNode of all sequential patches.
    // Execute their insert hooks in sequence, that is, execute the mounted hooks of the VMS corresponding to those component vNodes
    for(let i = 0; i< queue.length; ++i) {
      // Mounted hook
      queue[i].data.hook.insert(queue[i])
    }
  }
}

function initComponent(vnode, insertedVnodeQueue) {
  // The EL corresponding to the vnode has been generated
  if(vnode.data.pendingInsert) {
    / / update the insertedVnodeQueue
    insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)
    vnode.data.pendingInsert = null
  }
  
  vnode.elm = vnode.componentInstance.$el
  
  if(isPatchable(vnode)) {
    invokeCreateHooks(vnode, insertedVnodeQueue)
  } else {
    registerRef(vnode)
    insertedVnodeQueue.push(vnode)
  }
}

function invokeCreateHooks (vnode, insertedVnodeQueue) {
  for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {
    cbs.create[i$1](emptyNode, vnode);
  }
  i = vnode.data.hook; // Reuse variable
  if (isDef(i)) {
    if (isDef(i.create)) { i.create(emptyNode, vnode); }
    if (isDef(i.insert)) { insertedVnodeQueue.push(vnode); }
  }
}

componentVNodeHooks.insert = function insert(vnode) {
  const componentInstance = vnode.componentInstance

  if(! componentInstance._isMounted) { componentInstance._isMounted =true
    callHook(componentInstance, 'mounted')}}Copy the code

Patch creates a ELM for vNode, and this elm is inserted into its parent element. So executing invokeInsertHook after createElm inside the Patch function is a good time.

InvokeInsertHook (vnode, Queue, isInitialPatch) actually does two things, when vnode is a child component vnode and is the first patch, Save the patched component vNode queue (queue) on vnode.data.peningInsert, otherwise all component VNodes are patched into real nodes and inserted into their parent node. Insert Hook(callHook(componentInstance, ‘Mounted ‘)) for vNodes in the queue.

PendingInsert for each component vnode, vnode.data.pendingInsert stores the current vnode patch, which contains the queue of subcomponent VNodes that have been patched. This is then pushed by invokeCreateHooks into the parent component vNode’s pendingInsert, Finally, all vNodes are pushed into the insertedVnodeQueue variable created when the root component’s patches are pushed in their patch order. Obviously, a component leaf vNode’s data.pendingInsert is [].

Starting from App, all component VNodes start patch successively according to the depth-first traversal principle, but the parent component is not considered to be patch completed until all the child components are completed by patch. Finally, all component VNodes form an orderly queue. Is stored in the insertedVnodeQueue that the root component started patch with. Mount Component ($mount) {/ / mount component ($mount) {/ / mount component ($mount) {/ / mount component ($mount) {/ / mount component ($mount) {/ / mount component ($mount) {/ / mount component ($mount); The mounted hook of the root component is raised at the end of this function.