vuejs/core
runtime-dom
Active contributors: Evan You, edison, daiwei
Purpose
@vue/runtime-dom adapts @vue/runtime-core to the browser. It supplies the nodeOps and patchProp implementations the renderer needs, ships DOM-only built-in components (Transition, TransitionGroup), DOM-only directives (v-show, v-model for various form controls), and the public createApp/createSSRApp entry points. It is also the home of the Custom Elements integration (defineCustomElement).
The package is small relative to runtime-core (~5,000 lines, 17 files): the heavy lifting is in core; this package mostly bridges to the DOM.
Directory layout
packages/runtime-dom/src/
├── index.ts # exports + createApp/createSSRApp
├── nodeOps.ts # createElement/createText/insert/remove/setText/...
├── patchProp.ts # dispatcher: class | style | event | attr | prop
├── apiCustomElement.ts # defineCustomElement, VueElement, useShadowRoot, useHost (~22KB)
├── jsx.ts # JSX type definitions for HTML/SVG/MathML elements (~48KB)
├── modules/
│ ├── attrs.ts # patchAttr (DOM attributes)
│ ├── class.ts # patchClass
│ ├── events.ts # patchEvent (delegated, with timestamp dedup)
│ ├── props.ts # patchDOMProp (DOM properties like value, checked)
│ └── style.ts # patchStyle
├── components/
│ ├── Transition.ts # CSS transitions + JS hooks
│ └── TransitionGroup.ts # FLIP move animations
├── directives/
│ ├── vModel.ts # vModelText, vModelCheckbox, vModelRadio, vModelSelect, vModelDynamic
│ ├── vOn.ts # withModifiers, withKeys
│ └── vShow.ts # display: none toggle
└── helpers/
├── useCssModule.ts # SFC <style module> binding
└── useCssVars.ts # SFC v-bind in CSSKey abstractions
| Symbol | File | Description |
|---|---|---|
createApp / createSSRApp |
packages/runtime-dom/src/index.ts |
The two public app constructors. Lazily instantiate a renderer or hydration renderer on first call. |
render |
packages/runtime-dom/src/index.ts |
Direct render entry, used by app.mount and tests. |
nodeOps |
packages/runtime-dom/src/nodeOps.ts |
Implements RendererOptions's create/insert/remove/parentNode for DOM nodes. |
patchProp |
packages/runtime-dom/src/patchProp.ts |
Dispatcher: looks at the prop name and delegates to the right module. |
patchEvent |
packages/runtime-dom/src/modules/events.ts |
Attaches a delegated invoker on the element with a timestamp-based dedupe to prevent stale handlers running for events fired before the new one was attached. |
Transition |
packages/runtime-dom/src/components/Transition.ts |
DOM transition wrapper around BaseTransition; resolves CSS class names per phase. |
TransitionGroup |
packages/runtime-dom/src/components/TransitionGroup.ts |
List transitions with FLIP move animations. |
vModelText, vModelCheckbox, vModelRadio, vModelSelect, vModelDynamic |
packages/runtime-dom/src/directives/vModel.ts |
Per-input-type two-way binding. The compiler picks one based on the host element. |
vShow |
packages/runtime-dom/src/directives/vShow.ts |
Toggles style.display while preserving the original value. |
withModifiers, withKeys |
packages/runtime-dom/src/directives/vOn.ts |
Compose modifier wrappers for v-on (.stop, .prevent, .self, .once, .passive, .capture, key filters). |
defineCustomElement, VueElement |
packages/runtime-dom/src/apiCustomElement.ts |
Wrap a Vue component as a Web Component. Uses Shadow DOM by default. |
How createApp works
// packages/runtime-dom/src/index.ts
let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer
function ensureRenderer() {
return (
renderer ||
(renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
)
}
export const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args)
// override mount() to accept a CSS selector or an element/ShadowRoot
// and inject in-DOM template support
…
return app
}) as CreateAppFunction<Element>Two things to notice:
- The renderer is built lazily. Importing
createAppdoes not instantiate the renderer; importing only reactivity utilities does not pull innodeOpsorpatchProp. This is what enables tree-shaking in builds that don't render anything. app.mountis wrapped to add browser-specific behavior: support for selector strings, copying in-DOM templates as a fallback when notemplate/renderis supplied (with a__UNSAFE__comment about XSS risk), and stampingdata-v-appon the root for SSR detection.
createSSRApp is the same with ensureHydrationRenderer() instead. Once hydration is enabled the cached renderer flips to the hydration variant.
patchProp dispatch
packages/runtime-dom/src/patchProp.ts is a small if/else chain that picks one of the modules:
| Prefix / Predicate | Module | What it does |
|---|---|---|
class |
modules/class.ts |
Sets el.className (or setAttribute('class', …) for SVG). |
style |
modules/style.ts |
Object/string styles; preserves CSSText fallback for non-listed props. |
onXxx |
modules/events.ts |
Attaches a single delegated invoker keyed by event name; updates the value via the invoker so subsequent updates don't pay the listener add/remove cost. |
forceAttr |
modules/attrs.ts |
DOM attribute (camelCase → kebab-case for SVG/MathML). |
| Boolean attr / known DOM prop | modules/props.ts |
Sets the DOM property directly (e.g., el.value, el.checked). |
The dispatcher also handles namespaced (xlink:, xmlns:) attributes for SVG and MathML.
Events
patchEvent (modules/events.ts) is the most subtle module. It:
- Looks up or creates a single invoker function on the element.
- Stores the user handler on the invoker's
valueproperty. - The invoker uses
e.timeStampto ignore events that fired before it was patched in — necessary becauseaddEventListenersemantics combined with the microtask scheduler can otherwise replay events to the new handler. - Caches
getNow()per-element with a switch betweenDate.now()andperformance.now()based on the document's createEvent timeStamp.
This is the kind of code where a perf or correctness PR usually starts with git log -p packages/runtime-dom/src/modules/events.ts to read the bug fixes that produced the current shape.
v-model directives
The compiler (packages/compiler-dom/src/transforms/vModel.ts) inspects the host element/component and rewrites v-model to one of:
vModelText—<input type="text">,<textarea>. Listens toinput(orchangeif.lazy).vModelCheckbox— checkboxes. Handles array value and single boolean.vModelRadio— radios. ComparesvaluevialooseEqual.vModelSelect— selects. Reads<option>children, supports multiple.vModelDynamic— used when the element type isn't known until runtime (<input :type="...">).
Each directive ships SSR initializers via initVModelForSSR to keep server-rendered markup consistent.
Custom Elements
apiCustomElement.ts (~22KB) wraps a Vue component as a customElements.define-compatible class. The VueElement class extends HTMLElement, observes attributes, mounts the component into either Shadow DOM or light DOM (shadowRoot: false), and implements lifecycle (connectedCallback, disconnectedCallback, attributeChangedCallback). useShadowRoot() and useHost() are composables that return the host element/shadow root for the current custom-element instance.
SSR initialization
A subtle bit of plumbing: initDirectivesForSSR (gated by __SSR__) registers the SSR-side renderers for vModel and vShow. The server-renderer package imports this at module load (import 'vue') so SSR output for v-model and v-show matches the client.
Integration points
runtime-coreis the only direct dependency; everything in this package re-exportsruntime-coresoimport { ref, computed, watch, … } from 'vue'works throughruntime-dom.vue-compatreusesruntime-domand bundles the Vue 2-compat code fromruntime-core/src/compat.compiler-domproduces render functions that emit calls tovModelXxx,vShow,withModifiers,withKeys,Transition, andTransitionGroup— all of which are re-exported here.server-rendererimportsruntime-dom's SSR-init function so directive output matches client output.
Entry points for modification
- DOM bug? Most fixes live in
modules/(events.ts is the most-edited file in the package). - New transition behavior?
components/Transition.tsfor the wrapper,BaseTransitionin runtime-core for the core hook system. - New form-element binding? Add a
vModelXxxindirectives/vModel.tsand teachcompiler-dom'svModeltransform to pick it. - Custom element regression?
apiCustomElement.tsis a single self-contained file.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.