The data structure design of the virtual DOM
Core Concepts of Virtual DOM
The Virtual DOM is a lightweight JavaScript object representation of the real DOM. In Vue 3, the Virtual DOM has undergone significant refactoring compared to Vue 2, primarily optimizing performance and reducing memory usage. The core of the Virtual DOM is a tree-like structure, where each node corresponds to an element or component in the real DOM.
interface VNode {
type: string | Symbol | Component
props: Record<string, any> | null
children: VNode[] | string | null
el: Node | null
key: string | number | symbol | null
// ...other internal properties
}
Basic Node Structure
Vue 3's virtual node (VNode) structure is more streamlined than Vue 2's. A basic VNode contains the following key properties:
type
: Identifies the node type, which can be an HTML tag name, component, or special identifier.props
: Includes attributes, event listeners, etc.children
: An array of child nodes or text content.el
: A reference to the real DOM element.key
: A unique identifier for the diff algorithm.
// Example: Creating a simple div VNode
const vnode = {
type: 'div',
props: {
id: 'app',
class: 'container',
onClick: () => console.log('clicked')
},
children: [
{ type: 'span', props: null, children: 'Hello' },
{ type: 'span', props: null, children: 'World' }
],
el: null,
key: null
}
Static Node Optimization
Vue 3 applies special handling to static nodes through the hoistStatic
optimization to improve performance. Static nodes are hoisted outside the render function during the compilation phase to avoid repeated creation.
// Template before compilation
const template = `<div><span>Static Content</span></div>`
// Compiled render function
const _hoisted_1 = /*#__PURE__*/_createVNode("span", null, "Static Content")
function render() {
return _createVNode("div", null, [_hoisted_1])
}
Component Node Structure
Component-type VNodes have special processing logic. The type
property of a component VNode points to the component definition object, and props
contains the properties passed to the component.
const MyComponent = {
props: ['msg'],
setup(props) {
return () => _createVNode('div', null, props.msg)
}
}
const componentVNode = {
type: MyComponent,
props: { msg: 'Hello Component' },
children: null,
// ...other properties
}
Fragment Nodes
Vue 3 supports fragment nodes, allowing components to return multiple root nodes. The type
of a fragment node is a special Fragment
identifier.
const fragmentVNode = {
type: Symbol.for('v-fgt'),
props: null,
children: [
{ type: 'div', props: null, children: 'First' },
{ type: 'div', props: null, children: 'Second' }
],
// ...other properties
}
Dynamic Node Marking
Vue 3 introduces the patchFlag
system to mark dynamic content, optimizing the diff process. The compiler analyzes the template and generates appropriate flags.
const dynamicVNode = {
type: 'div',
props: { class: dynamicClass },
children: dynamicText,
patchFlag: 1 /* TEXT */ | 2 /* CLASS */,
dynamicProps: ['class'],
// ...other properties
}
Slot Node Handling
Vue 3 has refactored the slot system, with slot content represented as a special VNode type. Scoped slots are represented as functions.
const slotVNode = {
type: Symbol.for('v-slt'),
children: {
default: (props) => _createVNode('div', null, props.text)
}
}
Static Hoisting and Caching
Vue 3 analyzes static parts of the template and hoists them, while also caching event handlers to avoid unnecessary recreation.
// Template before compilation
const template = `<div @click="handleClick">Click me</div>`
// Compiled render function
const _hoisted_1 = { onClick: _cache[1] || (_cache[1] = ($event) => handleClick($event)) }
function render() {
return _createVNode("div", _hoisted_1, "Click me")
}
Dynamic Child Node Optimization
For dynamic child nodes, Vue 3 generates a block
tree structure, tracking only dynamic child nodes to reduce the scope of diffing.
const blockVNode = {
type: 'div',
dynamicChildren: [
{ type: 'span', patchFlag: 1 /* TEXT */, children: dynamicText }
],
// ...other properties
}
Built-in Component Handling
Built-in components like <KeepAlive>
, <Transition>
, etc., have special VNode types and processing logic.
const keepAliveVNode = {
type: KeepAlive,
props: { include: /foo-/ },
children: {
default: () => componentVNode
}
}
Async Component Nodes
Async components have different VNode representations before and after resolution. Vue 3 provides a more flexible async component API.
const asyncComponentVNode = {
type: defineAsyncComponent({
loader: () => import('./MyComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent
}),
props: { /* ... */ },
// ...other properties
}
Impact of the Composition API
The Composition API changes the internal structure of components but has little impact on the basic representation of VNodes. The render function returned by the setup
function still generates standard VNodes.
const setupComponent = {
setup() {
const count = ref(0)
return () => _createVNode('button', {
onClick: () => count.value++
}, count.value)
}
}
Compiler Optimization and VNode Generation
Vue 3's compiler analyzes templates and generates optimized VNode creation code, including static hoisting, patchFlag
marking, etc.
// Template before compilation
const template = `
<div>
<span v-if="show">Hello</span>
<span v-else>World</span>
</div>
`
// Compiled render function
function render(_ctx) {
return _createVNode("div", null, [
_ctx.show
? _createVNode("span", { key: 0 }, "Hello")
: _createVNode("span", { key: 1 }, "World")
])
}
Special Handling for Server-Side Rendering
During server-side rendering (SSR), VNode generation and processing differ to avoid browser-specific operations.
const ssrVNode = {
type: 'div',
props: { id: 'app' },
children: 'Server Rendered Content',
// Server-specific properties
ssrNode: null,
ssrContext: {}
}
Custom Renderer Support
Vue 3's Virtual DOM design supports custom renderers by abstracting platform-specific operations.
const nodeOps = {
createElement(tag) { /* ... */ },
insert(child, parent) { /* ... */ },
// ...other operations
}
const renderer = createRenderer(nodeOps)
renderer.render(vnode, container)
Enhanced Type System
Vue 3 is rewritten in TypeScript, with more precise VNode type definitions to help catch errors during development.
interface VNodeNormalizedChildren {
[key: string]: VNodeChild
}
type VNodeChild =
| VNode
| string
| number
| boolean
| null
| undefined
| VNodeArrayChildren
interface VNodeArrayChildren extends Array<VNodeChild> {}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:与Vue2响应式系统的对比
下一篇:patch算法的核心流程