Vue.js Core Initialization and Rendering Lifecycle
Global API Configuration
The framework initialization begins by attaching static utilities to the constructor. This includes configuration objects, helper functions, and global methods such as component registration, directive definition, and filter management. Essential reactive utilities like set, delete, and nextTick are also exposed at this level.
Prototype Extensions via Mixins
Several mixin functions extend the constructor's prototype to share behavior across instances:
- Initialization Mixin: Injects the internal
_initmethod responsible for bootstrapping the instance. - State Mixin: Defines reactive accessors for
$data,$props, and mutation methods like$setand$watch. - Events Mixin: Implements the event bus logic with
$on,$emit,$off, and$once. - Lifecycle Mixin: Adds methods for DOM updates and instance teardown, specifically
_update,$forceUpdate, and$destroy. - Render Mixin: Attaches helper functions for template rendering (e.g., slot resolution, list rendering, filter application) and the core
_rendermethod.
Instance Construction Flow
Invoking the constructor trigggers the internal initialization sequence. A proxy may be applied to the instance for development warnings. The core process involves several distinct setup phases:
1. Lifecycle and Event Setup
The instance receives references to its hierarchy ($parent, $children, $root) and initializes internal event listeners. Render slots and scoped slots are prepared, ensuring attributes and listeners are reactive without triggering dependency collection prematurely.
2. Hook Execution and Injection
The beforeCreate hook fires before state initialization. Dependency injection is processed next, making provided values available to the component without making them reactive.
3. State Initialization
This phase sets up the reactive system for various options:
- Props: Incoming properties are validated and observed. Each key is defined reactively, and values are proxied onto the instance.
- Methods: Functions are bound to the instance context.
- Data: The data object is passed through the observer system. A recursive walk defines getters and setters for each property to track dependencies.
- Computed and Watch: Computed properties are defined as lazy watchers, while user-defined watchers are instantiated to observe specific paths.
After state setup, the provide option is processed, and the created hook is invoked.
Compilation and Mounting
Calling $mount initiates the rendering process. If a template is provided, it is compiled into render functions.
Template Compilation
The compiler transforms template strings into executable JavaScript. The process involves parsing the template into an Abstract Syntax Tree (AST), optimizing static nodes, and generating code.
function generateRenderLogic (template, config) {
const ast = parseTemplate(template.trim(), config);
if (config.optimize !== false) {
optimizeStaticNodes(ast, config);
}
const code = generateCode(ast, config);
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
};
}
The resulting code is wrapped in a with(this) block to access instance properties directly.
Component Mounting
The mountComponent function orchestrates the update cycle. It triggers the beforeMount hook and defines an update function that calls _render followed by _update.
const renderTask = function () {
componentInstance._update(
componentInstance._render(),
hydrating
);
};
Dependency Tracking and Watchers
A watcher is created to manage the rendering lifecycle. When the update function executes, it acceses reactive properties, triggering getters.
Dependency Collection
Accessing a reactive property calls dep.depend(). If a target watcher exists, it adds itself to the dependency subs.
Dependency.prototype.collect = function () {
if (ActiveTarget) {
ActiveTarget.registerDependency(this);
}
};
The watcher maintains a list of dependencies to notify them of changes later.
Virtual DOM Patching
Once the render function returns a Virtual Node (VNode), the _update method patches the real DOM.
Node Creation
If no previous node exists, a new element is created. The patching process handles scope IDs for CSS, creates child nodes recursively, and checks for duplicate keys in lists.
function insertNode (vnode, queue, parent, anchor) {
if (vnode.type === COMPONENT) {
instantiateComponent(vnode);
} else {
createDOMElement(vnode, queue, parent, anchor);
}
}
Component Instantiation
If a VNode represents a child component, the initialization process repeats recursively for that child. Finally, outdated nodes are removed from the DOM to reflect the current state.