Jesse A. Green / notebook

Vue.js 2 Administrator & Developer Guide

Generated May 2026 from vuejs/vue at 9e88707

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Architecture Overview

Vue.js 2.7.16 is a progressive JavaScript framework for building user interfaces. It implements a reactive data-binding system powered by Object.defineProperty getter/setter interception, a virtual DOM diffing engine, and a template compiler that transforms HTML-based templates into render functions. The framework is designed as a monorepo of four packages that work together: the core runtime, a template compiler, a Single File Component (SFC) compiler, and a server-side renderer.

Vue uses a mixin-based architecture where the Vue constructor function is progressively enhanced through a series of *Mixin calls (initMixin, stateMixin, eventsMixin, lifecycleMixin, renderMixin) that attach prototype methods and instance initialization logic. The Global API (Vue.use, Vue.extend, Vue.mixin, Vue.component, Vue.directive, Vue.filter) is installed separately via initGlobalAPI.

Version 2.7 is the final minor release of Vue 2 and backports the Composition API from Vue 3 (ref, reactive, computed, watch, setup(), lifecycle hooks, dependency injection, effect scope) while maintaining full backward compatibility with the Vue 2 Options API.

Architecture Diagram

┌─────────────────────────────────────────────────────────────────┐
│                         Vue Instance                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────────────┐  │
│  │ initMixin│  │stateMixin│  │eventsMixin│  │lifecycleMixin  │  │
│  │ (_init)  │  │($data,   │  │($on,$off, │  │($forceUpdate,  │  │
│  │          │  │ $watch)  │  │ $emit)    │  │ $destroy)      │  │
│  └──────────┘  └──────────┘  └──────────┘  └────────────────┘  │
│  ┌────────────────┐                                              │
│  │  renderMixin   │                                              │
│  │  ($nextTick,   │                                              │
│  │   _render)     │                                              │
│  └────────────────┘                                              │
├─────────────────────────────────────────────────────────────────┤
│                        Global API                                │
│  Vue.use ─ Vue.mixin ─ Vue.extend ─ Vue.component               │
│  Vue.directive ─ Vue.filter ─ Vue.set ─ Vue.delete               │
│  Vue.nextTick ─ Vue.observable ─ Vue.compile                     │
├─────────────────────────────────────────────────────────────────┤
│                      Core Systems                                │
│  ┌──────────────┐  ┌────────────┐  ┌──────────────────────┐    │
│  │   Observer   │  │  Compiler  │  │    Virtual DOM        │    │
│  │   (reactive  │  │  (template │  │    (VNode, patch,     │    │
│  │    system)   │  │   → render │  │     diff engine)      │    │
│  │   Dep ←→     │  │   fns)     │  │                       │    │
│  │   Watcher    │  │            │  │                       │    │
│  └──────────────┘  └────────────┘  └──────────────────────┘    │
├─────────────────────────────────────────────────────────────────┤
│                    V3 Compat Layer (src/v3/)                     │
│  ref ─ reactive ─ computed ─ watch ─ provide/inject              │
│  effectScope ─ defineComponent ─ defineAsyncComponent ─ h        │
│  onMounted ─ onUnmounted ─ onBeforeMount ─ onUpdated ─ etc.     │
├─────────────────────────────────────────────────────────────────┤
│                      Platform Layer                              │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │  Web Platform (src/platforms/web/)                        │   │
│  │  DOM operations ─ v-model ─ v-show ─ transitions          │   │
│  │  class/style/attrs/events modules ─ compiler directives   │   │
│  └──────────────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────┤
│                    Packages (monorepo)                            │
│  @vue/compiler-sfc ─ vue-server-renderer ─ vue-template-compiler │
└─────────────────────────────────────────────────────────────────┘

Component Inventory

Component Path Role
Core Runtime src/core/ Vue constructor, reactivity, lifecycle, events, global API
Observer System src/core/observer/ Object.defineProperty-based reactive data tracking
Virtual DOM src/core/vdom/ VNode creation, patch/diff algorithm, component lifecycle
Template Compiler src/compiler/ HTML parser → AST → code generator (render functions)
Web Platform src/platforms/web/ DOM operations, platform-specific directives, transition modules
V3 Compat src/v3/ Composition API backport (reactivity, lifecycle, DI)
SFC Compiler packages/compiler-sfc/ .vue file parsing, <script setup>, scoped CSS
Server Renderer packages/server-renderer/ SSR rendering (string and stream), bundle renderer
Template Compiler (pkg) packages/template-compiler/ Standalone template compilation package
Shared Utilities src/shared/ Constants (LIFECYCLE_HOOKS, ASSET_TYPES), utility functions

Technology Stack

Layer Technology
Language TypeScript 4.8
Build System Rollup 2.79 with TypeScript plugin
Package Manager pnpm 8.9.2 (workspace monorepo)
Test Framework Vitest 1.0 (unit/integration), Karma + Jasmine (transition), Puppeteer (e2e)
Type Extraction Microsoft API Extractor
Formatting Prettier 2.6
Git Hooks yorkie (lint-staged on pre-commit, commit message validation)

2. System Requirements

Runtime Requirements

Requirement Specification
Browser IE9+ (with Object.defineProperty polyfill for IE8-), all modern browsers
ES Features Object.defineProperty (core reactivity), Proxy not required
Node.js (SSR) 12+

Development Requirements

Requirement Specification
Node.js 14+ recommended (for build tooling)
pnpm 8.9.2+ (specified in packageManager field)
TypeScript 4.8+ (included as devDependency)

Production Dependencies

Package Version Purpose
@vue/compiler-sfc workspace:* SFC compilation
csstype ^3.1.0 CSS type definitions for TypeScript

SFC Compiler Dependencies

Package Version Purpose
@babel/parser ^7.23.5 JavaScript/TypeScript parsing for <script setup>
postcss ^8.4.14 CSS processing for <style> blocks
source-map ^0.6.1 Source map generation

Server Renderer Dependencies

Package Version Purpose
chalk ^4.1.2 Colored console output
hash-sum ^2.0.0 Fast non-crypto hashing for component caching
he ^1.2.0 HTML entity encoding/decoding
lodash.template ^4.5.0 Template rendering for SSR HTML shell
lodash.uniq ^4.5.0 Array deduplication
resolve ^1.22.0 Module resolution for bundle renderer
serialize-javascript ^6.0.0 Safe serialization of state to HTML
source-map 0.5.6 Source map support for bundle renderer

3. Configuration Reference

Vue.config Options

The Vue.config object is the central runtime configuration. Properties are set individually — never replace the entire object.

Option Type Default Category Description
optionMergeStrategies Object {} User Custom merge strategies for mixin/extend option merging. Key = option name, value = merge function (parent, child, vm) => merged
silent boolean false User Suppress all Vue warnings
productionTip boolean true (dev only) User Display production mode tip on boot. Automatically false in production builds
devtools boolean true (dev only) User Enable Vue DevTools inspection. Automatically false in production builds
performance boolean false User Enable component init/compile/render performance tracing via performance.mark API
errorHandler Function|null null User (err, vm, info) => void — global handler for uncaught errors during rendering, watchers, and lifecycle hooks
warnHandler Function|null null User (msg, vm, trace) => void — custom handler for Vue development warnings
ignoredElements Array<string|RegExp> [] User Patterns for elements Vue should treat as external (Web Components)
keyCodes Object {} User Custom key aliases for v-on key modifiers
async boolean true Private Enable async update batching. Set false only for testing (via Vue Test Utils). Setting to false significantly reduces performance.
isReservedTag Function Platform-specific Platform (tag) => boolean — whether a tag is a reserved HTML/SVG element
isReservedAttr Function Platform-specific Platform (attr) => boolean — whether an attribute is reserved
isUnknownElement Function Platform-specific Platform (tag) => boolean — whether a tag is an unknown element
getTagNamespace Function noop Platform Returns namespace for SVG/MathML elements
parsePlatformTagName Function identity Platform Platform-specific tag name transformation
mustUseProp Function Platform-specific Platform Whether an attribute must be bound as a DOM property
_lifecycleHooks Array<string> LIFECYCLE_HOOKS constant Legacy Exposed list of lifecycle hook names

Important

Platform-level configuration options (isReservedTag, mustUseProp, etc.) are overwritten by the web platform layer at initialization. They are intended for platform integrators building custom renderers — not for application developers.

Environment Variables

Variable Location Purpose
NODE_ENV process.env Controls development vs. production mode. Production mode strips warnings and dev-only code paths
VUE_ENV process.env Set to 'server' automatically by vue-server-renderer to enable SSR-specific code paths

Feature Flags (Compile-Time)

Flag Default Purpose
NEW_SLOT_SYNTAX true Enable v-slot unified slot syntax
VBIND_PROP_SHORTHAND false Enable .prop shorthand for v-bind

These are defined in scripts/feature-flags.js and injected at build time via Rollup's @rollup/plugin-replace.

Note

Feature flags are compile-time constants evaluated during the Vue build process itself. They cannot be changed at runtime. They affect the source code that is included in the distribution bundles.

Build Configuration (scripts/config.js)

Vue's build system uses Rollup with multiple output targets. The scripts/build.js script reads configuration from scripts/config.js to produce all distribution variants:

Target Entry Format Output
full-dev entry-runtime-with-compiler.ts UMD dist/vue.js
full-prod entry-runtime-with-compiler.ts UMD (minified) dist/vue.min.js
runtime-cjs-dev entry-runtime.ts CommonJS dist/vue.runtime.common.dev.js
runtime-cjs-prod entry-runtime.ts CommonJS (minified) dist/vue.runtime.common.prod.js
runtime-esm entry-runtime-esm.ts ES Module dist/vue.runtime.esm.js
full-esm entry-runtime-with-compiler-esm.ts ES Module dist/vue.esm.js
full-esm-browser-dev entry-runtime-with-compiler-esm.ts ES Module (browser) dist/vue.esm.browser.js
full-esm-browser-prod entry-runtime-with-compiler-esm.ts ES Module (browser, minified) dist/vue.esm.browser.min.js
runtime-mjs entry-runtime-esm.ts ES Module (Node) dist/vue.runtime.mjs
server-renderer server-renderer/src/index.ts CommonJS packages/server-renderer/build.dev.js
compiler entry-compiler.ts CommonJS packages/template-compiler/build.js

Package Exports (package.json)

{
  "main": "dist/vue.runtime.common.js",
  "module": "dist/vue.runtime.esm.js",
  "unpkg": "dist/vue.js",
  "jsdelivr": "dist/vue.js",
  "typings": "types/index.d.ts",
  "exports": {
    ".": {
      "types": "./types/index.d.ts",
      "import": {
        "node": "./dist/vue.runtime.mjs",
        "default": "./dist/vue.runtime.esm.js"
      },
      "require": "./dist/vue.runtime.common.js"
    },
    "./compiler-sfc": {
      "types": "./compiler-sfc/index.d.ts",
      "import": "./compiler-sfc/index.mjs",
      "require": "./compiler-sfc/index.js"
    },
    "./dist/*": "./dist/*",
    "./types/*": ["./types/*.d.ts", "./types/*"],
    "./package.json": "./package.json"
  }
}

4. Deployment & Scaling

Build for Production

# Build all distribution variants
node scripts/build.js

# Build specific targets
node scripts/build.js -- runtime-cjs,server-renderer

# Development mode with watch
npm run dev
# Expected output structure after build
dist/
├── vue.js                      # Full build (UMD, dev)
├── vue.min.js                  # Full build (UMD, prod)
├── vue.common.js               # Full (CJS)
├── vue.runtime.common.js       # Runtime (CJS, default main)
├── vue.runtime.esm.js          # Runtime (ESM, default module)
├── vue.esm.js                  # Full (ESM)
├── vue.esm.browser.js          # Full (ESM, browser dev)
├── vue.esm.browser.min.js      # Full (ESM, browser prod)
└── vue.runtime.mjs             # Runtime (MJS, Node ESM)

Server-Side Rendering Deployment

Vue 2's SSR works through vue-server-renderer, which provides two rendering modes:

Basic Renderer (no webpack integration):

const { createBasicRenderer } = require('vue-server-renderer')
const renderer = createBasicRenderer()

renderer.renderToString(app, (err, html) => {
  // html is the rendered markup
})

Full Renderer (with template and client manifest):

const { createRenderer } = require('vue-server-renderer')

const renderer = createRenderer({
  template: '<!DOCTYPE html><html><head>{{{ renderResourceHints() }}}{{{ renderStyles() }}}</head><body><!--vue-ssr-outlet--></body></html>',
  clientManifest // from vue-server-renderer/client-plugin
})

renderer.renderToString(app, context, (err, html) => {
  // html includes template with injected assets
})

Bundle Renderer (webpack integration with source maps):

const { createBundleRenderer } = require('vue-server-renderer')

const renderer = createBundleRenderer(serverBundle, {
  runInNewContext: false, // recommended for performance
  template,              // HTML template
  clientManifest         // for automatic asset injection
})

Tip

Use runInNewContext: false (or 'once') for the bundle renderer in production. Creating a new context for every request has significant performance overhead.

Streaming:

const stream = renderer.renderToStream(app, context)
stream.pipe(res)

Warning

Function templates (dynamic template resolution) are only supported with renderToString, not renderToStream. Using a function template with streaming throws: function template is only supported in renderToString.

SSR Renderer Options

Option Type Default Description
template string | Function — HTML template with <!--vue-ssr-outlet--> marker
inject boolean true Auto-inject CSS/assets from client manifest
cache Object — Component-level cache (get/set required, has optional)
clientManifest Object — Client build manifest for automatic asset injection
shouldPreload Function — (file, type) => boolean — control <link rel="preload">
shouldPrefetch Function — (file, type) => boolean — control <link rel="prefetch">
basedir string — Base directory for resolving bundle renderer resources
runInNewContext boolean | 'once' true Sandbox evaluation context for bundle renderer
serializer Function serialize-javascript Custom state serialization function
directives Object — Server-side directive implementations
modules Array — Custom render modules

SSR Cache Configuration

const renderer = createRenderer({
  cache: {
    get(key, cb) {
      cb(myCache.get(key))
    },
    set(key, val) {
      myCache.set(key, val)
    },
    has(key, cb) {
      cb(myCache.has(key))
    }
  }
})

Important

The cache object must implement at least get and set methods. An error is thrown at startup if these are missing: renderer cache must implement at least get & set.

Webpack Plugins for SSR

vue-server-renderer provides webpack plugins for automatic manifest generation:

// webpack.client.config.js
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
module.exports = {
  plugins: [new VueSSRClientPlugin()]
}

// webpack.server.config.js
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
module.exports = {
  plugins: [new VueSSRServerPlugin()]
}

5. Operations

Build Scripts

Script Command Purpose
build node scripts/build.js Build all distribution variants
build:ssr npm run build -- runtime-cjs,server-renderer Build SSR-specific bundles
build:types rimraf temp && tsc --declaration --emitDeclarationOnly --outDir temp && api-extractor run Generate rolled-up type declarations
dev rollup -w -c scripts/config.js --environment TARGET:full-dev Watch mode for full build
dev:cjs rollup -w -c scripts/config.js --environment TARGET:runtime-cjs-dev Watch mode for CJS runtime
dev:esm rollup -w -c scripts/config.js --environment TARGET:runtime-esm Watch mode for ESM runtime
dev:ssr rollup -w -c scripts/config.js --environment TARGET:server-renderer Watch mode for server renderer
dev:compiler rollup -w -c scripts/config.js --environment TARGET:compiler Watch mode for template compiler

Test Scripts

Script Command Purpose
test Full test suite ts-check → types → unit → e2e → ssr → sfc
test:unit vitest run test/unit Unit tests
test:ssr npm run build:ssr && vitest run server-renderer SSR tests (requires build first)
test:sfc vitest run compiler-sfc SFC compiler tests
test:e2e npm run build -- full-prod,server-renderer-basic && vitest run test/e2e E2E tests (requires build)
test:transition karma start test/transition/karma.conf.js Transition tests (Karma + Chrome)
test:types npm run build:types && tsc -p ./types/tsconfig.json TypeScript type tests

Note

The full test suite runs TypeScript checking, type tests, unit tests, e2e tests, SSR tests, and SFC compiler tests sequentially. Individual test commands can be run independently for faster feedback.

Release Process

The release process is automated via scripts/release.js:

  1. Version bump (patch/minor/major via interactive prompt)
  2. Run full test suite
  3. Build all distribution variants
  4. Generate changelog (conventional-changelog)
  5. Commit, tag, and push
  6. Publish to npm

Git Hooks

Hook Script Purpose
pre-commit lint-staged Run Prettier on staged .js/.ts files
commit-msg scripts/verify-commit-msg.js Validate conventional commit format

Tip

Git hooks are managed by yorkie (a husky fork). The configuration is in package.json under the gitHooks key.

6. Performance

Reactivity Performance Characteristics

Vue 2's reactivity system is based on Object.defineProperty, which has specific performance characteristics:

Observer initialization: When a component is created, Vue walks all data properties and converts them to getter/setter pairs. For large objects with many properties, this incurs a one-time cost proportional to the number of properties.

Dependency tracking: Each reactive property has a Dep instance. When a property is accessed during render, the current Watcher is added to the Dep's subscriber list. This is O(1) per property access.

Dep subscriber cleanup: When a subscriber is removed (component destroyed), the Dep sets the subscriber slot to null rather than splicing the array immediately — this is a performance optimization for deps with massive numbers of subscribers (GitHub issue #12696). Cleanup happens on the next scheduler flush via cleanupDeps().

Array reactivity: Vue patches the prototype of reactive arrays to intercept mutation methods (push, pop, shift, unshift, splice, sort, reverse). If __proto__ is not available, the methods are defined directly on the array instance.

Async update batching: By default (Vue.config.async = true), multiple data changes within the same event loop tick are batched into a single DOM update. Watchers are queued and sorted by id to ensure correct execution order (parent before child).

Build Size Optimization

Strategy Impact
Use runtime-only build ~30% smaller than full build
Production mode (NODE_ENV=production) Strips dev warnings, component traces
sideEffects: false in package.json Enables tree-shaking in bundlers
Object.freeze() on large static data Skips observation (see __v_skip flag)

SSR Performance

Component-level caching reduces rendering time for expensive components. Components must have a unique name and a serverCacheKey function:

export default {
  name: 'Item',
  serverCacheKey(props) {
    return `${props.id}::${props.lastUpdated}`
  }
}

renderToStream provides streaming output for better Time to First Byte compared to renderToString.

7. Security

Template Security

Vue templates are compiled to render functions. User input rendered via {{ }} interpolation is automatically HTML-escaped by Vue's toString() implementation, which converts values to strings using JSON.stringify for objects/arrays or String() for primitives.

Caution

v-html renders raw HTML and bypasses Vue's automatic escaping. Never use v-html with untrusted user input — it creates XSS vulnerabilities. If you must render user-provided HTML, sanitize it server-side first.

SSR Security

The server renderer uses serialize-javascript to safely serialize application state into the HTML page. This prevents XSS through state injection by escaping </script> tags and other dangerous sequences.

// Custom serializer (if needed)
const renderer = createRenderer({
  serializer: (state) => {
    return myCustomSerializer(state, { isJSON: true })
  }
})

Warning

When embedding serialized state in HTML templates, ensure the template uses {{{ }}} (triple-mustache / raw) for the state injection point so that the serialized output is not double-escaped.

Render Proxy (Development)

In development mode, Vue wraps component instances in a Proxy (when available) that warns about:

  • Access to undefined properties on the render context
  • Mutations during rendering
  • Reserved property names ($, _ prefix)

This proxy is stripped in production builds.

Bundle Renderer Sandboxing

The createBundleRenderer evaluates server bundles in a sandboxed context by default (runInNewContext: true). Each request gets a fresh global context. Options:

Value Behavior Performance
true New vm.Context per request Slowest, most isolated
'once' One shared context, created once Medium
false Run in current process context Fastest, shared global state

8. Developer Internals

Repository Structure

vuejs/vue/
├── src/                            # Core source code
│   ├── compiler/                   # Template compiler
│   │   ├── parser/                 # HTML parser → AST
│   │   │   ├── index.ts            # Main parser (parse())
│   │   │   ├── html-parser.ts      # Tokenizer for HTML
│   │   │   ├── text-parser.ts      # Mustache interpolation parser
│   │   │   ├── filter-parser.ts    # Pipe filter expression parser
│   │   │   └── entity-decoder.ts   # HTML entity decoding
│   │   ├── codegen/                # AST → render function code
│   │   │   ├── index.ts            # Main code generator
│   │   │   └── events.ts           # Event handler code generation
│   │   ├── directives/             # Compiler directive transforms
│   │   │   ├── bind.ts             # v-bind compilation
│   │   │   ├── model.ts            # v-model compilation
│   │   │   └── on.ts               # v-on compilation
│   │   ├── optimizer.ts            # Static tree hoisting
│   │   ├── create-compiler.ts      # Compiler factory
│   │   ├── to-function.ts          # Compile string → function
│   │   ├── error-detector.ts       # Template expression validation
│   │   ├── codeframe.ts            # Error location display
│   │   ├── helpers.ts              # Shared compiler utilities
│   │   └── index.ts                # Compiler entry (baseCompile)
│   ├── core/                       # Core runtime
│   │   ├── config.ts               # Vue.config definition
│   │   ├── index.ts                # Core entry (initGlobalAPI)
│   │   ├── global-api/             # Vue.use, Vue.mixin, Vue.extend, etc.
│   │   ├── instance/               # Instance init, state, events, lifecycle, render
│   │   ├── observer/               # Reactivity: Observer, Dep, Watcher, scheduler
│   │   ├── vdom/                   # Virtual DOM: VNode, patch, createElement
│   │   ├── components/             # Built-in components (KeepAlive)
│   │   └── util/                   # Debug, env detection, error handling, nextTick
│   ├── platforms/web/              # Web platform layer
│   │   ├── compiler/               # Web-specific compiler modules
│   │   ├── runtime/                # DOM operations, directives, transitions
│   │   ├── util/                   # Web utilities (attrs, class, style, element)
│   │   └── entry-*.ts              # Build entry points
│   ├── shared/                     # Shared constants and utilities
│   │   ├── constants.ts            # LIFECYCLE_HOOKS, ASSET_TYPES, SSR_ATTR
│   │   └── util.ts                 # Utility functions (extend, cached, looseEqual)
│   ├── v3/                         # Vue 3 Composition API backport
│   │   ├── reactivity/             # ref, reactive, computed, readonly, effectScope
│   │   ├── sfc-helpers/            # useCssModule, useCssVars
│   │   ├── apiWatch.ts             # watch, watchEffect, watchPostEffect, watchSyncEffect
│   │   ├── apiLifecycle.ts         # onMounted, onUnmounted, etc.
│   │   ├── apiInject.ts            # provide, inject
│   │   ├── apiSetup.ts             # setup(), SetupContext, useSlots, useAttrs
│   │   ├── apiAsyncComponent.ts    # defineAsyncComponent
│   │   ├── h.ts                    # h() render helper
│   │   ├── currentInstance.ts      # getCurrentInstance
│   │   ├── debug.ts                # DebuggerOptions, DebuggerEvent
│   │   └── index.ts                # V3 API exports + defineComponent
│   └── types/                      # Internal TypeScript type definitions
├── packages/
│   ├── compiler-sfc/               # @vue/compiler-sfc
│   │   └── src/
│   │       ├── parse.ts            # SFC file parser
│   │       ├── parseComponent.ts   # Component descriptor extraction
│   │       ├── compileScript.ts    # <script setup> compilation
│   │       ├── compileTemplate.ts  # <template> compilation
│   │       ├── compileStyle.ts     # <style> compilation (scoped, CSS vars)
│   │       ├── cssVars.ts          # v-bind() in CSS support
│   │       ├── rewriteDefault.ts   # Default export rewriting
│   │       ├── prefixIdentifiers.ts # Identifier prefixing for setup
│   │       ├── babelUtils.ts       # Babel AST walking utilities
│   │       ├── stylePreprocessors.ts # Less/Sass/Stylus support
│   │       ├── types.ts            # SFC-specific types
│   │       ├── warn.ts             # SFC-specific warnings
│   │       ├── stylePlugins/       # PostCSS plugins (scoped, trim)
│   │       ├── templateCompilerModules/ # Asset URL/srcset transforms
│   │       └── index.ts            # Public API exports
│   ├── server-renderer/            # vue-server-renderer
│   │   └── src/
│   │       ├── index.ts            # createRenderer, createBundleRenderer
│   │       ├── create-renderer.ts  # Full renderer factory
│   │       ├── create-basic-renderer.ts # Basic renderer (no template)
│   │       ├── render.ts           # Component rendering logic
│   │       ├── render-context.ts   # SSR render context (caching, nesting)
│   │       ├── render-stream.ts    # Readable stream implementation
│   │       ├── write.ts            # Write function with backpressure
│   │       ├── compiler.ts         # SSR-optimized compiler
│   │       ├── util.ts             # SSR utilities
│   │       ├── bundle-renderer/    # Webpack bundle evaluation + source maps
│   │       ├── template-renderer/  # HTML template rendering + asset injection
│   │       ├── optimizing-compiler/ # SSR-specific AST optimizer
│   │       ├── modules/            # SSR attribute rendering (attrs, class, style)
│   │       ├── directives/         # SSR directive implementations (v-show, v-model)
│   │       └── webpack-plugin/     # Client/server webpack plugins
│   └── template-compiler/          # vue-template-compiler
├── types/                          # Public TypeScript declarations
├── test/                           # Test suites
│   ├── unit/                       # Unit tests (Vitest)
│   ├── e2e/                        # E2E tests (Puppeteer)
│   ├── transition/                 # Transition tests (Karma)
│   └── helpers/                    # Test utilities
├── scripts/                        # Build, release, config
├── benchmarks/                     # Performance benchmarks
├── examples/                       # Example applications
├── compiler-sfc/                   # Re-export shim for vue/compiler-sfc
└── dist/                           # Built output (gitignored)

Architecture Deep Dive

Instance Initialization Flow

When new Vue(options) is called, the following initialization sequence executes in _init():

  1. Assign UID — each instance gets a unique monotonically-increasing _uid
  2. Mark as Vue instance — set _isVue = true and __v_skip = true (skip observation)
  3. Create effect scope — new EffectScope(true) for Composition API compatibility
  4. Merge options — mergeOptions() merges constructor options with instance options using optionMergeStrategies
  5. Init proxy (dev only) — wrap instance in a Proxy for render context validation
  6. initLifecycle(vm) — set up parent/child references ($parent, $root, $children, $refs)
  7. initEvents(vm) — initialize event system (_events, _hasHookEvent)
  8. initRender(vm) — set up $createElement, $attrs, $listeners
  9. callHook('beforeCreate')
  10. initInjections(vm) — resolve inject options from parent chain
  11. initState(vm) — initialize props, methods, data, computed, watch
  12. initProvide(vm) — resolve provide option
  13. callHook('created')
  14. Auto-mount — if vm.$options.el is provided, call vm.$mount(el)

Reactivity Deep Dive

The reactivity system consists of three core classes:

Observer (src/core/observer/index.ts):

  • Attached to each observed object/array via __ob__ property
  • Walks all properties of objects and calls defineReactive() for each
  • For arrays: patches the prototype chain to intercept mutation methods (push, pop, shift, unshift, splice, sort, reverse)
  • Uses hasProto check — if __proto__ is available, patches the prototype; otherwise defines methods directly on the array instance
  • Tracks the number of VMs using this object as root $data (vmCount)

Dep (src/core/observer/dep.ts):

  • Dependency container — each reactive property getter creates a Dep
  • Maintains a subscriber list (subs: Array<DepTarget | null>)
  • depend() — adds the current Dep.target (active watcher) to subscribers
  • notify() — triggers update() on all subscribers
  • Subscriber removal uses null-marking (not splice) for performance with large subscriber lists, with deferred cleanup on next scheduler flush

Watcher (src/core/observer/watcher.ts):

  • Represents a reactive computation (render function, computed property, or user watcher)
  • get() — evaluates the getter, collecting dependencies via pushTarget/popTarget
  • update() — queued to the scheduler (batched async flush) unless sync
  • teardown() — removes from all deps and marks inactive

The reactive cycle:

Data Change → setter → dep.notify() → watcher.update()
  → queueWatcher(watcher) → nextTick → flushSchedulerQueue()
  → watcher.run() → watcher.get() → re-render / re-compute
  → getter → dep.depend() → add watcher to dep.subs

Compiler Pipeline

The template compiler transforms HTML templates into JavaScript render functions through three phases:

  1. Parse (src/compiler/parser/index.ts): Uses an HTML tokenizer (html-parser.ts) to parse the template into an Abstract Syntax Tree (AST). The parser handles:

    • Element nodes, text nodes, expressions
    • Directives (v-if, v-for, v-bind, v-on, v-model, v-slot)
    • Filters (text-parser.ts, filter-parser.ts)
    • HTML entities (entity-decoder.ts)
  2. Optimize (src/compiler/optimizer.ts): Walks the AST and marks static subtrees — nodes that never change. Static trees are hoisted out of the render function and reused across re-renders, avoiding unnecessary diffing.

  3. Generate (src/compiler/codegen/index.ts): Transforms the AST into a render function string. The CodegenState class manages:

    • Static render function array (staticRenderFns)
    • Directive code generation
    • Event handler code generation (events.ts)
    • Slot scope code generation

Compiler factory pattern: createCompilerCreator(baseCompile) returns a createCompiler(baseOptions) function. This allows different platforms (web, server) to provide platform-specific options while sharing the core compilation pipeline.

Virtual DOM

Vue uses a virtual DOM for efficient UI updates:

  • VNode (src/core/vdom/vnode.ts): Lightweight JavaScript object representing a DOM element. Contains tag, data, children, key, componentOptions, etc.
  • createElement (src/core/vdom/create-element.ts): Public API for creating VNodes. Normalizes children, resolves components, handles functional components.
  • patch (src/core/vdom/patch.ts): The diff/patch algorithm. Compares old and new VNode trees and applies minimal DOM mutations. Uses a two-pass algorithm for children diffing.
  • Component VNodes (src/core/vdom/create-component.ts): Creates component VNodes with componentOptions containing the constructor, props, listeners, and children.

Platform Layer

The web platform (src/platforms/web/) adapts the core to the browser:

Runtime modules (applied during patching):

Module File Purpose
attrs runtime/modules/attrs.ts HTML attribute updates
class runtime/modules/class.ts CSS class updates (static + dynamic)
dom-props runtime/modules/dom-props.ts DOM property updates (value, innerHTML)
events runtime/modules/events.ts DOM event listener management
style runtime/modules/style.ts Inline style updates (object/array/string)
transition runtime/modules/transition.ts CSS/JS transition integration

Runtime directives:

Directive File Purpose
v-model runtime/directives/model.ts Two-way binding for form elements
v-show runtime/directives/show.ts Toggle element visibility via CSS display

Runtime components:

Component File Purpose
<transition> runtime/components/transition.ts Single-element enter/leave transitions
<transition-group> runtime/components/transition-group.ts List transitions with FLIP animations

Module Reference

src/core/config.ts — Vue.config

Defines the Config interface and default configuration object. Divided into three categories:

  • User-facing: optionMergeStrategies, silent, productionTip, devtools, performance, errorHandler, warnHandler, ignoredElements, keyCodes
  • Platform-internal: isReservedTag, isReservedAttr, parsePlatformTagName, isUnknownElement, getTagNamespace, mustUseProp
  • Private: async (update batching), _lifecycleHooks (legacy exposure)

src/core/instance/index.ts — Vue Constructor

The Vue function is defined as a plain function (not a class) to allow mixin-based enhancement:

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

Enhanced by five mixin functions that add prototype methods:

  • initMixin → _init()
  • stateMixin → $data, $props, $set, $delete, $watch
  • eventsMixin → $on, $once, $off, $emit
  • lifecycleMixin → _update, $forceUpdate, $destroy
  • renderMixin → $nextTick, _render, plus render helpers

src/core/global-api/index.ts — initGlobalAPI

Installs global methods and properties on the Vue constructor:

  • Vue.config (read-only getter, dev warning on set)
  • Vue.util (internal: warn, extend, mergeOptions, defineReactive)
  • Vue.set, Vue.delete, Vue.nextTick
  • Vue.observable — makes a plain object reactive
  • Vue.options — base options container (components, directives, filters)
  • Built-in component registration (KeepAlive)
  • initUse(Vue) → Vue.use
  • initMixin(Vue) → Vue.mixin
  • initExtend(Vue) → Vue.extend
  • initAssetRegisters(Vue) → Vue.component, Vue.directive, Vue.filter

src/core/observer/ — Reactivity System

Files:

  • index.ts — Observer class, observe(), defineReactive(), set(), del()
  • dep.ts — Dep class (dependency container), pushTarget, popTarget
  • watcher.ts — Watcher class (reactive computation)
  • scheduler.ts — Async flush scheduler (queueWatcher, flushSchedulerQueue)
  • traverse.ts — Deep dependency traversal for deep watchers
  • array.ts — Array mutation method patching

Key implementation details:

  • defineReactive uses closure over a Dep instance and the property's current value
  • shouldObserve flag allows temporarily disabling observation (used during prop validation)
  • __v_skip flag on an object prevents observation (used by Vue instances themselves)
  • SSR mock reactivity uses a no-op Dep to avoid unnecessary tracking

src/v3/ — Composition API

Reactivity primitives:

  • ref.ts — ref(), shallowRef(), toRef(), toRefs(), unref(), customRef(), triggerRef(), proxyRefs(), proxyWithRefUnwrap()
  • reactive.ts — reactive(), shallowReactive(), markRaw(), toRaw(), isReactive(), isReadonly(), isShallow(), isProxy()
  • readonly.ts — readonly(), shallowReadonly()
  • computed.ts — computed() with getter/setter support
  • effectScope.ts — effectScope(), onScopeDispose(), getCurrentScope()
  • operations.ts — TrackOpTypes, TriggerOpTypes enums

API layer:

  • apiWatch.ts — watch(), watchEffect(), watchPostEffect(), watchSyncEffect(), internal doWatch()
  • apiLifecycle.ts — onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured, onServerPrefetch, onRenderTracked, onRenderTriggered
  • apiInject.ts — provide(), inject() with prototype-chain-based inheritance
  • apiSetup.ts — initSetup(), SetupContext creation, useSlots(), useAttrs(), useListeners() (Vue 2 only), mergeDefaults()
  • apiAsyncComponent.ts — defineAsyncComponent() with loading/error/timeout support (note: suspensible option warns and is ignored in Vue 2)
  • h.ts — h() function wrapping createElement
  • currentInstance.ts — getCurrentInstance(), setCurrentInstance()

packages/compiler-sfc/src/ — SFC Compiler

Public API exports:

Function Purpose
parse(source, options) Parse .vue file into SFCDescriptor
compileTemplate(options) Compile <template> block to render function
compileStyle(options) Compile <style> block (scoped CSS, CSS modules)
compileStyleAsync(options) Async style compilation (for preprocessors)
compileScript(descriptor, options) Compile <script setup> to standard script
rewriteDefault(input, as) Rewrite export default to named variable
parseComponent(content, options) Legacy component parsing (backward compat for fork-ts-checker-webpack-plugin)
generateCodeFrame(source, start, end) Generate error code frame

PostCSS plugins:

  • scoped.ts — Adds [data-v-{hash}] attribute selectors for scoped styles
  • trim.ts — Trims empty CSS rules

Template compiler modules:

  • assetUrl.ts — Transforms src attributes to require() calls
  • srcset.ts — Transforms srcset attributes

Style preprocessor support: The SFC compiler supports Less, Sass/SCSS, Stylus, and PostCSS via the stylePreprocessors.ts module. These are optional dependencies loaded dynamically.

packages/server-renderer/src/ — Server Renderer

Core rendering:

  • create-renderer.ts — createRenderer() with renderToString and renderToStream
  • create-basic-renderer.ts — Simplified renderer without template/manifest support
  • render.ts — Component render logic with error handling
  • render-context.ts — RenderContext class managing nesting and caching state
  • render-stream.ts — RenderStream extending Node.js Readable
  • write.ts — Write function with backpressure handling

Bundle renderer:

  • create-bundle-renderer.ts — Evaluates webpack-compiled server bundles
  • create-bundle-runner.ts — Module runner with optional sandboxing
  • source-map-support.ts — Stack trace rewriting for source maps

Optimizing compiler:

  • index.ts — SSR-specific createCompiler with optimizing codegen
  • optimizer.ts — Marks SSR-optimizable subtrees
  • codegen.ts — SSR-specific code generation (string concatenation instead of VNode creation)
  • modules.ts — SSR render modules for attrs/class/style
  • runtime-helpers.ts — Runtime helpers for SSR-optimized rendering

Code Patterns and Conventions

Mixin pattern: Vue's constructor is a plain function enhanced by *Mixin functions rather than a class hierarchy. This allows fine-grained prototype augmentation and avoids class-related limitations.

Compiler creator pattern: createCompilerCreator(baseCompile) creates a factory that produces platform-specific compilers. This allows the web and SSR compilers to share parsing/codegen while customizing options.

Cached function pattern: cached(fn) wraps a pure function with a string-keyed cache. Used extensively for string transformations (camelize, capitalize, hyphenate).

makeMap pattern: Generates O(1) lookup functions from comma-separated strings. Used for checking built-in tags, reserved attributes, etc.

Feature flag pattern: Compile-time boolean flags (__DEV__, NEW_SLOT_SYNTAX, VBIND_PROP_SHORTHAND) are replaced by Rollup's @rollup/plugin-replace during build, enabling dead code elimination.

Atomic state transitions: The observer system uses Dep.target as a global singleton — only one watcher can be evaluated at a time. The pushTarget/popTarget stack ensures correct nesting during recursive component evaluation.

Reserved property prefixes: Properties starting with $ (public API) or _ (internal) on component instances are reserved. Variables in setup() starting with these prefixes trigger a development warning.

Lifecycle Hook Constants

Defined in src/shared/constants.ts:

export const LIFECYCLE_HOOKS = [
  'beforeCreate', 'created',
  'beforeMount', 'mounted',
  'beforeUpdate', 'updated',
  'beforeDestroy', 'destroyed',
  'activated', 'deactivated',
  'errorCaptured', 'serverPrefetch',
  'renderTracked', 'renderTriggered'
] as const

Asset Types

export const ASSET_TYPES = ['component', 'directive', 'filter'] as const

These drive the creation of Vue.component(), Vue.directive(), and Vue.filter() registration methods.

Testing Strategy

Unit tests (test/unit/, Vitest):

  • modules/observer/ — reactivity system (Observer, Dep, Watcher, scheduler)
  • modules/compiler/ — parser, codegen, optimizer, code frame
  • modules/vdom/ — createElement, patch, DOM modules
  • modules/util/ — error handling, nextTick, toString
  • features/ — directives, components, options, lifecycle, filters, global API
  • features/v3/ — Composition API (reactivity, setup, lifecycle hooks, inject, watch)

E2E tests (test/e2e/, Puppeteer + Vitest):

  • Tests against example applications (commits, grid, markdown, SVG, TodoMVC, tree)
  • SSR hydration test (basic-ssr)
  • Async edge cases

Transition tests (test/transition/, Karma + Jasmine):

  • CSS transitions, JavaScript hooks
  • Transition groups, FLIP animations
  • Transition modes (in-out, out-in)
  • Transition with <keep-alive>

SFC compiler tests (packages/compiler-sfc/test/, Vitest):

  • Component parsing, script compilation (<script setup>)
  • Style compilation (scoped CSS, CSS vars)
  • Template compilation, identifier prefixing

SSR tests (packages/server-renderer/test/, Vitest):

  • String rendering, stream rendering
  • Bundle renderer with webpack fixtures
  • Template rendering with asset injection
  • Reactivity in SSR context

Type tests (types/test/, TypeScript compiler):

  • Options API type checking
  • Vue 3 defineComponent types
  • Async component types
  • Plugin types
  • Reactivity types

Potential Improvements

Based on the codebase analysis:

  1. Object.defineProperty limitations — The Vue 2 reactivity system cannot detect property addition/deletion or array index mutation. Vue 3 solves this with Proxy. For Vue 2, Vue.set/Vue.delete remain necessary.

  2. Dep subscriber cleanup — The null-marking optimization (GitHub #12696) trades memory for CPU during bulk subscriber removal. For applications with very dynamic component trees, this could accumulate null entries.

  3. Nested <pre> tag handling — Template compiler has known issues with nested <pre> tags (GitHub #12965).

  4. $scopedSlots re-rendering — Scoped slots may not re-render when passing $scopedSlots to children (GitHub #13066).

  5. Strict template checking — Vue 2.7 strictTemplates option has compatibility issues with some editor tooling (GitHub #13104).

  6. TypeScript AsyncComponentFactory type — The type definition for async component factories is incorrect per GitHub #10252.

Contributing

Development setup:

git clone https://github.com/vuejs/vue.git
cd vue
pnpm install
npm run dev

Code style: Prettier auto-formatting is enforced on commit via lint-staged. TypeScript files use the project's tsconfig.json (target: es5, module: es2015, strict mode).

Commit conventions: Commit messages are validated by scripts/verify-commit-msg.js. Follow the Angular commit convention (feat:, fix:, docs:, style:, refactor:, test:, chore:).

Testing changes:

# Run unit tests
npm run test:unit

# Run specific test file
npx vitest run test/unit/features/directives/model-text.spec.ts

# Type check
npm run ts-check

Note

SSR and E2E tests require a preceding build step. Run npm run build:ssr before npm run test:ssr, and the full build before npm run test:e2e.

9. Integration Points

SFC Tooling Integration

@vue/compiler-sfc is the primary integration point for build tools:

const { parse, compileScript, compileTemplate, compileStyle } = require('vue/compiler-sfc')

// 1. Parse the .vue file
const { descriptor } = parse(source)

// 2. Compile <script setup>
const scriptResult = compileScript(descriptor, { id: scopeId })

// 3. Compile <template>
const templateResult = compileTemplate({
  source: descriptor.template.content,
  compiler: require('vue-template-compiler'),
  compilerOptions: { /* ... */ }
})

// 4. Compile <style> (with scoping)
const styleResult = compileStyle({
  source: descriptor.styles[0].content,
  scoped: descriptor.styles[0].scoped,
  id: scopeId
})

Vue DevTools Integration

Vue exposes window.__VUE_DEVTOOLS_GLOBAL_HOOK__ for DevTools communication. When Vue.config.devtools is true (default in development), Vue:

  • Emits lifecycle events to the devtools hook
  • Exposes component tree for inspection
  • Enables performance timing marks

vue-template-compiler Standalone API

const compiler = require('vue-template-compiler')

// Compile template to render function
const { render, staticRenderFns, errors } = compiler.compile('<div>{{ msg }}</div>')

// Parse .vue file (legacy)
const parsed = compiler.parseComponent(sfcSource, { pad: 'line' })

Plugin Integration

Plugins receive the Vue constructor and can:

  • Add global methods/properties (Vue.prototype.$method)
  • Add global assets (Vue.directive, Vue.component, Vue.filter)
  • Add instance methods via Vue.mixin
  • Add component options via Vue.mixin
const MyPlugin = {
  install(Vue, options) {
    // Plugin has full access to Vue API
  }
}

Note

Vue.use() prevents duplicate plugin installation by tracking installed plugins in this._installedPlugins. The plugin's install function (or the plugin function itself if no install method exists) is called with Vue as the first argument and any additional Vue.use() arguments spread after.