阿里云主机折上折
  • 微信号
Current Site:Index > The data structure design of the virtual DOM

The data structure design of the virtual DOM

Author:Chuan Chen 阅读数:22944人阅读 分类: Vue.js

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.