What is a VNode?
A VNode (Virtual Node) is Preact’s internal representation of a DOM element or component. Here’s how VNodes are created (src/create-element.js:47):
VNode structure
Each VNode contains:type- The element type (string for DOM elements, function for components)props- The element’s properties and attributeskey- A unique identifier for efficient list diffingref- A reference to the actual DOM node or component instance_children- Child VNodes_dom- The corresponding real DOM node_component- The component instance (for component VNodes)_parent- The parent VNode_depth- Depth in the VNode tree
Properties prefixed with
_ are internal to Preact and should not be accessed directly.How the Virtual DOM works
Preact’s Virtual DOM operates in three phases:- Render - Components return VNodes describing what the UI should look like
- Diff - Preact compares the new VNode tree with the previous one
- Commit - Only the differences are applied to the real DOM
The diff algorithm
The diff algorithm is the heart of Preact’s Virtual DOM. It’s implemented insrc/diff/index.js:58:
Diffing components
When diffing components, Preact determines whether to reuse an existing instance or create a new one (src/diff/index.js:100):
Preact differentiates between class and functional components by checking if
prototype.render exists.Diffing children
Child diffing is optimized using keys and a skew-based algorithm (src/diff/children.js:45):
Key-based reconciliation
Keys help Preact identify which items have changed, been added, or removed in lists:- Without keys
- With keys
Finding matching VNodes
Preact’s diff algorithm uses a sophisticated matching strategy (src/diff/children.js:400):
Efficient updates
Preact’s Virtual DOM provides several performance benefits:Batched updates
Multiple state changes are batched together into a single DOM update:Minimal DOM manipulation
Only the parts of the DOM that actually changed are updated:Smart component updates
Components can skip unnecessary renders usingshouldComponentUpdate():
Committing changes
After diffing, Preact commits the changes to the real DOM (src/diff/index.js:406):
- Applies refs to DOM nodes
- Invokes lifecycle callbacks (like
componentDidMount) - Handles errors gracefully
The commit phase is synchronous and cannot be interrupted, ensuring the DOM is always in a consistent state.