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, notrenderToStream. 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
getandsetmethods. 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:
- Version bump (patch/minor/major via interactive prompt)
- Run full test suite
- Build all distribution variants
- Generate changelog (
conventional-changelog) - Commit, tag, and push
- 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(ahuskyfork). The configuration is inpackage.jsonunder thegitHookskey.
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-htmlrenders raw HTML and bypasses Vue's automatic escaping. Never usev-htmlwith 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():
- Assign UID — each instance gets a unique monotonically-increasing
_uid - Mark as Vue instance — set
_isVue = trueand__v_skip = true(skip observation) - Create effect scope —
new EffectScope(true)for Composition API compatibility - Merge options —
mergeOptions()merges constructor options with instance options usingoptionMergeStrategies - Init proxy (dev only) — wrap instance in a
Proxyfor render context validation initLifecycle(vm)— set up parent/child references ($parent,$root,$children,$refs)initEvents(vm)— initialize event system (_events,_hasHookEvent)initRender(vm)— set up$createElement,$attrs,$listenerscallHook('beforeCreate')initInjections(vm)— resolveinjectoptions from parent chaininitState(vm)— initializeprops,methods,data,computed,watchinitProvide(vm)— resolveprovideoptioncallHook('created')- Auto-mount — if
vm.$options.elis provided, callvm.$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
hasProtocheck — 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 currentDep.target(active watcher) to subscribersnotify()— triggersupdate()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 viapushTarget/popTargetupdate()— queued to the scheduler (batched async flush) unless syncteardown()— 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:
-
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)
-
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. -
Generate (
src/compiler/codegen/index.ts): Transforms the AST into a render function string. TheCodegenStateclass manages:- Static render function array (
staticRenderFns) - Directive code generation
- Event handler code generation (
events.ts) - Slot scope code generation
- Static render function array (
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. Containstag,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 withcomponentOptionscontaining 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,$watcheventsMixin→$on,$once,$off,$emitlifecycleMixin→_update,$forceUpdate,$destroyrenderMixin→$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.nextTickVue.observable— makes a plain object reactiveVue.options— base options container (components,directives,filters)- Built-in component registration (
KeepAlive) initUse(Vue)→Vue.useinitMixin(Vue)→Vue.mixininitExtend(Vue)→Vue.extendinitAssetRegisters(Vue)→Vue.component,Vue.directive,Vue.filter
src/core/observer/ — Reactivity System
Files:
index.ts—Observerclass,observe(),defineReactive(),set(),del()dep.ts—Depclass (dependency container),pushTarget,popTargetwatcher.ts—Watcherclass (reactive computation)scheduler.ts— Async flush scheduler (queueWatcher,flushSchedulerQueue)traverse.ts— Deep dependency traversal for deep watchersarray.ts— Array mutation method patching
Key implementation details:
defineReactiveuses closure over aDepinstance and the property's current valueshouldObserveflag allows temporarily disabling observation (used during prop validation)__v_skipflag on an object prevents observation (used by Vue instances themselves)- SSR mock reactivity uses a no-op
Depto 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 supporteffectScope.ts—effectScope(),onScopeDispose(),getCurrentScope()operations.ts—TrackOpTypes,TriggerOpTypesenums
API layer:
apiWatch.ts—watch(),watchEffect(),watchPostEffect(),watchSyncEffect(), internaldoWatch()apiLifecycle.ts—onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onActivated,onDeactivated,onErrorCaptured,onServerPrefetch,onRenderTracked,onRenderTriggeredapiInject.ts—provide(),inject()with prototype-chain-based inheritanceapiSetup.ts—initSetup(),SetupContextcreation,useSlots(),useAttrs(),useListeners()(Vue 2 only),mergeDefaults()apiAsyncComponent.ts—defineAsyncComponent()with loading/error/timeout support (note:suspensibleoption warns and is ignored in Vue 2)h.ts—h()function wrappingcreateElementcurrentInstance.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 stylestrim.ts— Trims empty CSS rules
Template compiler modules:
assetUrl.ts— Transformssrcattributes torequire()callssrcset.ts— Transformssrcsetattributes
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()withrenderToStringandrenderToStreamcreate-basic-renderer.ts— Simplified renderer without template/manifest supportrender.ts— Component render logic with error handlingrender-context.ts—RenderContextclass managing nesting and caching staterender-stream.ts—RenderStreamextending Node.jsReadablewrite.ts— Write function with backpressure handling
Bundle renderer:
create-bundle-renderer.ts— Evaluates webpack-compiled server bundlescreate-bundle-runner.ts— Module runner with optional sandboxingsource-map-support.ts— Stack trace rewriting for source maps
Optimizing compiler:
index.ts— SSR-specificcreateCompilerwith optimizing codegenoptimizer.ts— Marks SSR-optimizable subtreescodegen.ts— SSR-specific code generation (string concatenation instead of VNode creation)modules.ts— SSR render modules for attrs/class/styleruntime-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 framemodules/vdom/— createElement, patch, DOM modulesmodules/util/— error handling, nextTick, toStringfeatures/— directives, components, options, lifecycle, filters, global APIfeatures/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
defineComponenttypes - Async component types
- Plugin types
- Reactivity types
Potential Improvements
Based on the codebase analysis:
-
Object.definePropertylimitations — The Vue 2 reactivity system cannot detect property addition/deletion or array index mutation. Vue 3 solves this withProxy. For Vue 2,Vue.set/Vue.deleteremain necessary. -
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.
-
Nested
<pre>tag handling — Template compiler has known issues with nested<pre>tags (GitHub #12965). -
$scopedSlotsre-rendering — Scoped slots may not re-render when passing$scopedSlotsto children (GitHub #13066). -
Strict template checking — Vue 2.7
strictTemplatesoption has compatibility issues with some editor tooling (GitHub #13104). -
TypeScript
AsyncComponentFactorytype — 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:ssrbeforenpm run test:ssr, and the full build beforenpm 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 inthis._installedPlugins. The plugin'sinstallfunction (or the plugin function itself if noinstallmethod exists) is called with Vue as the first argument and any additionalVue.use()arguments spread after.