CodeVetter · Repo Unpack · Behavior traces
vuejs/core
Three flows carry most of the system's weight: browser app startup, a reactive state change producing a re-render, and SFC compilation as invoked by a bundler plugin. A fourth, hydration, forks off startup at `createSSRApp`. The call chains below were read in order through runtime-dom → apiCreateApp → renderer → component → scheduler.
- Startup step 1: `createApp(RootComponent)` in runtime-dom calls `ensureRenderer()` (memoising a single module-level renderer created from `rendererOptions`), then in dev injects `isNativeTag` and the `compilerOptions` getter warnings before wrapping `app.mount`.
- Startup step 2: the wrapped `app.mount` calls `normalizeContainer` (a `document.querySelector` for string selectors, with a dev warning for closed ShadowRoots), copies `container.innerHTML` into `component.template` when the component has neither render nor template, clears `container.textContent`, calls the inner mount, then sets `data-v-app` and removes `v-cloak`.
- Startup step 3: the inner `mount` in apiCreateApp creates the root vnode via `createVNode(rootComponent, rootProps)`, stamps `vnode.appContext = context`, installs a dev-only `context.reload` HMR hook that re-renders a cloned vnode with `el = null`, then calls `render(vnode, rootContainer, namespace)` and finally sets `rootContainer.__vue_app__ = app` and calls `devtoolsInitApp`.
- Startup step 4: the renderer's `render` dispatches to `patch(container._vnode || null, vnode, ...)`, records `container._vnode = vnode`, and then — guarded by an `isFlushing` latch — drains `flushPreFlushCbs` and `flushPostFlushCbs` synchronously.
- Startup step 5: `patch` switches on vnode type (Text/Comment/Static/Fragment) and otherwise on `shapeFlag`, routing ELEMENT → `processElement`, COMPONENT → `processComponent`, TELEPORT → `TeleportImpl.process(..., internals)` and (under `__FEATURE_SUSPENSE__`) SUSPENSE → `SuspenseImpl.process(..., internals)`.
- Startup step 6: `mountComponent` calls `createComponentInstance`, registers HMR when `instance.type.__hmrId` exists, injects `internals` into the ctx when the vnode is a KeepAlive, calls `setupComponent(instance, false, optimized)`, and either registers the instance with the parent Suspense (when `instance.asyncDep` is set, inserting a Comment placeholder) or calls `setupRenderEffect`.
- Startup step 7: `setupComponent` runs `initProps` then `initSlots`, and for stateful components `setupStatefulComponent` creates `instance.accessCache`, wraps `instance.ctx` in `new Proxy(instance.ctx, PublicInstanceProxyHandlers)`, pauses tracking, and invokes `setup(props, setupContext)` through `callWithErrorHandling` with `ErrorCodes.SETUP_FUNCTION`.
- Startup step 8: `finishComponentSetup` performs on-the-fly compilation only when `!isSSR && compile && !Component.render`, merging app-level `compilerOptions`, component-level `compilerOptions` and `delimiters` into `finalCompilerOptions`, then installs the `RuntimeCompiledPublicInstanceProxyHandlers` proxy for `with`-block render functions and finally runs `applyOptions(instance)` when `__FEATURE_OPTIONS_API__`.
- Startup step 9: `setupRenderEffect`'s `componentUpdateFn` on first run fires `bm` hooks, then either hydrates (when `el && hydrateNode`, optionally deferring through `type.__asyncHydrate` for lazy-hydrated async components) or calls `renderComponentRoot(instance)` and patches; on completion it queues `onVnodeMounted`/`m`/`a` hooks via `queuePostRenderEffect`, sets `instance.isMounted = true` and nulls `initialVNode`/`container`/`anchor` to avoid mount-only memory leaks (#2458).
- Update trace: a reactive write calls `Dep.trigger` → subscriber `notify()` → `batch(sub)` unless the effect is already RUNNING without ALLOW_RECURSE; the component's effect scheduler calls `queueJob`, `queueFlush` schedules `flushJobs` on the microtask queue, and `flushJobs` re-enters `componentUpdateFn` on the update branch which resolves `next`, runs `bu`, re-renders and patches, then queues `u`.
- Hydration trace: `createSSRApp` → `ensureHydrationRenderer()` → `createHydrationRenderer(rendererOptions)` → `baseCreateRenderer(options, createHydrationFunctions)`, whose returned `[hydrate, hydrateNode]` pair is wired to the internals; `app.mount(container, true, ...)` then routes to `hydrate(vnode, rootContainer)` instead of `render`, and any structural divergence calls `logMismatchError()`.
- Full-build template trace: `compileToFunction` short-circuits on `compileCache[key]`, resolves a `#id` selector to `el.innerHTML` (annotated `__UNSAFE__`), defaults `isCustomElement` to `tag => !!customElements.get(tag)`, calls `compile(template, opts)`, then evaluates the generated code with `new Function('Vue', code)(runtimeDom)` and tags the result `_rc = true`.
- SFC trace: a bundler plugin calls `parse()` (LRU-cached) → `compileScript(descriptor, options)` for `<script setup>` → `compileTemplate` (which reuses the descriptor's template AST) → `compileStyle`/`compileStyleAsync` for scoped/module CSS; the playground's vite.config.ts wires exactly this by passing a real `fs` host into `@vitejs/plugin-vue`'s `script` option.