Frontend Development 12 min read

How Vue Transforms a .vue Component into a VNode

This article explains step by step how Vue.js converts a .vue component file into a virtual DOM node by using Vue.extend, createComponent, installComponentHooks, and the VNode constructor, illustrated with real source code snippets and detailed commentary.

Xueersi Online School Tech Team
Xueersi Online School Tech Team
Xueersi Online School Tech Team
How Vue Transforms a .vue Component into a VNode

In the previous article we covered Vue's virtual DOM and how vm._render turns JavaScript‑generated DOM data into a vnode; this piece focuses on the core concept of componentization, where a page is split into reusable .vue files that form a component tree.

To illustrate the component creation process, a simple demo is shown:

import Vue from 'vue'
import app from '@app'
Vue.config.productionTip = false
console.log(app)
new Vue({
  el: '#root',
  render: h => {
    const vnode = h(app)
    console.log(vnode)
    return vnode
  }
})
// app.vue

Running this demo shows that the app component is processed by Vue plugins into an object, which is later passed to the internal _createElement function defined in src/core/vdom/create-element.js . The function distinguishes between ordinary HTML tags and component tags, delegating the latter to createComponent .

export function createElement (context, tag, data, children, normalizationType, alwaysNormalize) {
  // ...
  if (typeof tag === 'string') {
    // ordinary HTML vnode (covered previously)
  } else {
    // component case
    vnode = createComponent(tag, data, context, children)
  }
  // ...
}

export function createComponent (Ctor, data, context, children, tag) {
  if (isUndef(Ctor)) return
  const baseCtor = context.$options._base
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor)
  }
  // ... validation, async handling, etc.
  installComponentHooks(data)
  const name = Ctor.options.name || tag
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )
  return vnode
}

The baseCtor is the Vue constructor itself ( Vue.options._base ), set during the global API initialization. When Ctor is an object, Vue calls baseCtor.extend(Ctor) , which creates a subclass constructor with its own options , cid , and prototype chain.

Vue.extend = 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
  // copy static assets, register self‑component, etc.
  cachedCtors[SuperId] = Sub
  return Sub
}

After the constructor is prepared, installComponentHooks merges Vue's built‑in component lifecycle hooks ( init , prepatch , insert , destroy ) into the vnode's data.hook object, ensuring that component‑specific behavior runs during the patch process.

function installComponentHooks (data) {
  const hooks = data.hook || (data.hook = {})
  for (let i = 0; i < hooksToMerge.length; i++) {
    const key = hooksToMerge[i]
    const existing = hooks[key]
    const toMerge = componentVNodeHooks[key]
    if (existing !== toMerge && !(existing && existing._merged)) {
      hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge
    }
  }
}

const componentVNodeHooks = {
  init (vnode, hydrating) { /* ... */ },
  prepatch (oldVnode, vnode) { /* ... */ },
  insert (vnode) { /* ... */ },
  destroy (vnode) { /* ... */ }
}
const hooksToMerge = Object.keys(componentVNodeHooks)

Finally, a new VNode instance is created with a tag like vue-component-1-MyComponent , the prepared data, and a componentOptions object that stores the constructor, props, listeners, and children. This vnode is later passed to vm._update for rendering into real DOM.

In summary, Vue creates a component vnode by (1) extending the base Vue constructor to obtain a component subclass, (2) initializing component options and installing lifecycle hooks, and (3) constructing a VNode that represents the component in the virtual DOM.

frontendJavaScriptComponentVuerendervnode
Xueersi Online School Tech Team
Written by

Xueersi Online School Tech Team

The Xueersi Online School Tech Team, dedicated to innovating and promoting internet education technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.