Frontend Development 16 min read

Unraveling Vue 3 Initialization: From createApp to VNode Rendering

This article walks through Vue 3's initialization process, explaining what createApp(App).mount('#app') does, how the runtime‑dom and runtime‑core modules create and render VNodes, and how patch, shapeFlags, and patchFlags drive efficient DOM updates.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
Unraveling Vue 3 Initialization: From createApp to VNode Rendering

Goal

Understand what

createApp(App).mount("#app")

actually does and explore Vue 3.0's initialization rendering process.

What You’ll Gain

Insight into Vue 3.0's initialization flow.

Direction for reading Vue 3.0 source code.

Getting Started

Clone the

vue-next

repository, enable source maps in

package.json

, and run

yarn dev

. This builds

vue.global.js

and its sourcemap, allowing step‑by‑step debugging of the call stack.

Runtime‑dom

The

createApp()

function originates from

runtime-dom

. It registers a

mount

method on the app instance, enabling a basic demo to run without errors.

<code>const app = {data(){return {counter:1}}}
Vue.createApp(app).mount("#app")</code>

Key functions include

ensureRenderer

(which obtains a renderer via

createRenderer

from

runtime-core

) and the platform‑specific

nodeOps

that wrap DOM APIs.

Runtime‑core

Inside

runtime-core

,

baseCreateRenderer

(≈2000 lines) builds the core rendering logic.

createApp

is created by

createAppAPI

, ultimately returning an app object with a

mount

method.

First Render: .mount("#app")

The demo uses the classic API. The

dom‑mount

implementation prepares the container, extracts the template if needed, clears the content, and calls the core

mount

function.

<code>const { mount } = app;
app.mount = (containerOrSelector) => {
  const container = normalizeContainer(containerOrSelector);
  if (!container) return;
  const component = app._component;
  if (!isFunction(component) && !component.render && !component.template) {
    component.template = container.innerHTML;
  }
  container.innerHTML = "";
  const proxy = mount(container);
  container.removeAttribute("v-cloak");
  return proxy;
};</code>

The core

mount

creates a root VNode, then calls

render

, which delegates to

patch

.

Creating the Root VNode

The VNode is initialized with properties such as

type

,

shapeFlag

,

patchFlag

, etc.

shapeFlag

uses bitwise enums (e.g., ELEMENT=1, STATEFUL_COMPONENT=4) to identify node kinds.

<code>export const enum ShapeFlags {
  ELEMENT = 1,
  FUNCTIONAL_COMPONENT = 1 << 1,
  STATEFUL_COMPONENT = 1 << 2,
  TEXT_CHILDREN = 1 << 3,
  ARRAY_CHILDREN = 1 << 4,
  // ... other flags
}</code>

Patch flags (e.g., TEXT=1, CLASS=1<<1) mark dynamic parts for optimized diffing.

<code>export const enum PatchFlags {
  TEXT = 1,
  CLASS = 1 << 1,
  STYLE = 1 << 2,
  PROPS = 1 << 3,
  FULL_PROPS = 1 << 4,
  // ... other flags
}</code>

Patch Process

patch

examines a VNode's

type

and

shapeFlag

to dispatch to specific processors (e.g.,

processElement

,

processComponent

,

processFragment

,

processText

, etc.). It handles mounting when no previous VNode exists and updating otherwise.

<code>if (n1 && !isSameVNodeType(n1, n2)) {
  // unmount old tree
  n1 = null;
}
const { type, shapeFlag } = n2;
switch (type) {
  case Text:
    processText(n1, n2, container, anchor);
    break;
  // ... other cases
  default:
    if (shapeFlag &amp; ShapeFlags.ELEMENT) {
      processElement(...);
    } else if (shapeFlag &amp; ShapeFlags.COMPONENT) {
      processComponent(...);
    }
}
</code>

Fragment, Teleport, and Suspense are new Vue 3 components, each identified by distinct

shapeFlag

values.

Component Mounting

During mounting, Vue creates a component internal instance, initializes props and slots, sets up reactivity, and compiles the template into a render function. The render function returns a VNode tree, which is then patched into the DOM.

<code>function componentEffect() {
  if (!instance.isMounted) {
    const subTree = (instance.subTree = renderComponentRoot(instance));
    patch(null, subTree, container);
    instance.isMounted = true;
  } else {
    // update path
  }
}
</code>

Reactivity is driven by

effect

, which tracks dependencies during the render and triggers updates when reactive state changes.

Summary

The article dissects Vue 3’s bootstrapping: cloning the source, enabling source maps, tracing

createApp

through

runtime-dom

and

runtime-core

, understanding VNode creation, the role of

shapeFlag

and

patchFlag

, and how the patch algorithm efficiently mounts and updates the DOM.

frontendRenderingruntimeVue.jspatchVue3vnode
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

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.