Vue 3's Speed Secrets: How Patch Flags and Static Hoisting Work?

Vue 3's Speed Secrets: How Patch Flags and Static Hoisting Work?

Hello there, tech enthusiasts!

If you've been working with Vue, you're probably aware that Vue 3 has brought a breath of fresh air in terms of performance. Beyond the Composition API or Teleport, one of the key factors that makes Vue 3 significantly faster than its predecessor is the improvements at the Compiler level. Today, we'll delve into two incredibly smart mechanisms that the Vue 3 Compiler uses to boost speed: Patch Flag and Static Hoisting.

Patch Flag: The "Specific Change Marker"

The Problem to Solve

In Vue 2, when a component's state changed, the diffing algorithm (virtual DOM comparison) would scan the entire VNode (Virtual Node) tree of that component to find differences and update the real DOM. This could be inefficient, especially for large components with many elements, most of which remained unchanged.

What is a Patch Flag?

Vue 3 addressed this problem more elegantly. During the compilation process, the Vue 3 Compiler analyzes your template and adds Patch Flags to VNodes. These flags are numerical values (bitwise enums) attached to VNodes, telling the runtime exactly which properties of that element might change and need to be checked during the next render.

Thanks to Patch Flags, the Vue runtime doesn't need to compare all properties of a VNode; it only needs to focus on the flagged properties. This helps skip unnecessary comparisons, accelerating DOM updates.

Common Patch Flag Types

  • TEXT (1): Only the text content inside changes.
  • CLASS (2): The class attribute changes.
  • STYLE (4): The style attribute changes.
  • PROPS (8): Only properties (props) other than class or style change.
  • FULL_PROPS (16): Unknown changing properties, forcing a full comparison.
  • HYDRATE_EVENTS (32): Only event listeners change (used only for SSR hydration).
  • STABLE_FRAGMENT (64): Fragment with unchanging child order.
  • KEYED_FRAGMENT (128): Fragment with keyed children whose order changes.
  • UNKEYED_FRAGMENT (256): Fragment with unkeyed children.
  • BAIL (512): Instructs the compiler to skip optimizations for this node.
  • DEV_ROOT_FRAGMENT (1024): For development mode, to mark the root fragment.

Illustrative Example

Suppose you have the following template:

<template>  <div :class="dynamicClass">    <p>Hello, <strong>{{ name }}</strong>!</p>    <span>This is a static text.</span>  </div></template>

When compiled, the Vue 3 Compiler will recognize:

  • The <div>: Has :class="dynamicClass", so it will be flagged with CLASS.
  • The <strong>: Has {{ name }}, so it will be flagged with TEXT.
  • The <p> and <span>: Have no dynamic attributes or direct dynamic content, and can be optimized in other ways (e.g., Static Hoisting).

When dynamicClass changes, Vue only needs to update the class attribute of the <div>. When name changes, Vue only needs to update the text content of the <strong>. Other non-dynamic parts are skipped, saving significant time.

Static Hoisting: "Lifting Up" Immutable Components

The Problem to Solve

Even with Patch Flags optimizing updates, there are still elements in a template that are completely static – meaning they never change after the initial render. In Vue 2, these elements were still recreated as VNodes on every component render, wasting CPU and memory resources.

What is Static Hoisting?

Static Hoisting is an optimization technique used by the Vue 3 Compiler to identify and "lift out" completely static VNodes or entire static VNode subtrees from the render function. Instead of recreating them on every render, these static VNodes are created only once during component initialization.

Subsequently, in future renders, the render function merely references these pre-created static VNodes, rather than creating new ones. This significantly reduces CPU workload and memory consumption, especially in applications with a lot of static content.

Illustrative Example

Let's revisit the previous example:

<template>  <div :class="dynamicClass">    <p>Hello, <strong>{{ name }}</strong>!</p>    <span>This is a static text.</span>    <!-- This part is entirely static -->    <footer>      <hr>      <small>Copyright 2023</small>    </footer>  </div></template>

In this example, the <footer> section, including <hr> and <small>Copyright 2023</small>, is entirely static. The Vue 3 Compiler will "hoist" this entire <footer> subtree out of the main render function. It will only be created as a VNode once. Every time the component re-renders, Vue will reuse this footer's VNode without recreating it, regardless of whether dynamicClass or name changes.

Summary and Impact

Patch Flags and Static Hoisting are two crucial pieces in Vue 3's performance optimization puzzle. They operate at the compiler level, helping the Vue runtime work much more efficiently:

  • Patch Flags: Make updates more "targeted," only checking and patching elements that are likely to change.
  • Static Hoisting: Eliminates the recreation of static elements on every render, saving system resources.

Thanks to these smart mechanisms, Vue 3 not only provides a better development experience but also ensures your applications run smoother and faster, especially on lower-spec devices or in large-scale applications.

Hopefully, this article has helped you better understand the "magic" behind Vue 3's superior performance. Keep exploring and make the most of this powerful framework!