In what aspects is the performance improvement of Vue3.0 mainly reflected?

1. Compilation stage

Looking back at Vue2, we know that each component instance corresponds to a watcher instance, which will use the data property during component rendering. Recorded as a dependency. When the dependency changes and setter is triggered, watcher will be notified, causing the associated component to re-render.

Just imagine, a component structure is as shown below

<template>
    <div id="content">
        <p class="text">Static text</p>
        <p class="text">Static text</p>
        <p class="text">{<!-- -->{ message }}</p>
        <p class="text">Static text</p>
        ...
        <p class="text">Static text</p>
    </div>
</template>

As you can see, there is only one dynamic node inside the component, and the rest are static nodes, so many diff and traversal are actually unnecessary, causing a waste of performance.

Therefore, Vue3 has been further optimized during the compilation phase. The main ones are as follows:

  • diff algorithm optimization
  • static boost
  • Event listening cache
  • SSR optimization
diff algorithm optimization

vue3 adds static tags in the diff algorithm compared to vue2

Regarding this static tag, its function is to add a flag tag to the place where changes will occur. The next time there is a change, directly look for that place for comparison.

In the figure below, the p tag that has been marked as a static node will not be compared during the diff process, further improving performance.

About static type enumeration as follows

export const enum PatchFlags {
  TEXT = 1, // dynamic text node
  CLASS = 1 << 1, // 2 dynamic class
  STYLE = 1 << 2, // 4 dynamic style
  PROPS = 1 << 3, // 8 dynamic properties, excluding class name and style
  FULL_PROPS = 1 << 4, // 16 dynamic keys, when the key changes, a complete diff algorithm is required for comparison
  HYDRATE_EVENTS = 1 << 5, // 32 represents nodes with event listeners
  STABLE_FRAGMENT = 1 << 6, // 64 A Fragment that does not change the order of child nodes
  KEYED_FRAGMENT = 1 << 7, // 128 Fragment with key attribute
  UNKEYED_FRAGMENT = 1 << 8, // 256 child nodes have no key Fragment
  NEED_PATCH = 1 << 9, // 512
  DYNAMIC_SLOTS = 1 << 10, // dynamic solt
  HOISTED = -1, // The special flag is a negative integer meaning it will never be used as diff
  BAIL = -2 // A special flag referring to the difference algorithm
}
Static promotion

In Vue3, elements that do not participate in updates will be statically promoted and will only be created once and reused directly during rendering.

This eliminates the need to repeatedly create nodes. Large applications will benefit from this change, eliminating repeated creation operations and optimizing memory usage during runtime.

<span>Hello</span>

<div>{<!-- -->{ message }}</div>

Before doing static promotion

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock(_Fragment, null, [
    _createVNode("span", null, "Hello"),
    _createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
  ], 64 /* STABLE_FRAGMENT */))
}

After doing static upgrade

const _hoisted_1 = /*#__PURE__*/_createVNode("span", null, "Hello", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock(_Fragment, null, [
    _hoisted_1,
    _createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
  ], 64 /* STABLE_FRAGMENT */))
}

// Check the console for the AST

The static content _hoisted_1 is placed outside the render function. You only need to get _hoisted_1 every time you render.

At the same time, _hoisted_1 is marked with PatchFlag, the static tag value is -1, and the special flag is a negative integer indicating that it will never be used for Diff

Event listening cache

By default, the binding event behavior will be regarded as dynamic binding, so its changes will be tracked every time

<div>
  <button @click = 'onClick'>Click me</button>
</div>

Event listener caching is not enabled

export const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", { onClick: _ctx.onClick }, "Click me", 8 /* PROPS */, ["onClick"])
                                             // PROPS=1<<3,// 8 //Dynamic properties, but does not include class name and style
  ]))
})

After turning on event listener caching

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))
    }, "I point")
  ]))
}

The above findings indicate that after caching is enabled, there are no static tags. In other words, use it directly next time you use the diff algorithm.

SSR optimization

When the static content reaches a certain level, the createStaticVNode method will be used to generate a static node on the client. These static node will be directly innerHtml code>, there is no need to create an object and then render it based on the object

div>
<div>
<span>Hello</span>
</div>
... // many static properties
<div>
<span>{<!-- -->{ message }}</span>
</div>
</div>

After compilation

import { mergeProps as _mergeProps } from "vue"
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "@vue/server-renderer"

export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {
  const _cssVars = { style: { color: _ctx.color }}
  _push(`<div${
    _ssrRenderAttrs(_mergeProps(_attrs, _cssVars))
  }><div><span>Hello</span>...<div><span>Hello</span><div><span>${
    _ssrInterpolate(_ctx.message)
  }</span></div></div>`)
}

2. Source code size

Compared with Vue2, the overall size of Vue3 has become smaller. In addition to removing some uncommon APIs, the most important thing is Tree shanking

Any function, such as ref, reavtived, computed, etc., is only packaged when it is used, and unused modules are shaken. Dropped, the overall packaged volume becomes smaller

import { computed, defineComponent, ref } from 'vue';
export default defineComponent({
    setup(props, context) {
        const age = ref(18)

        let state = reactive({
            name: 'test'
        })

        const readOnlyAge = computed(() => age.value + + ) // 19

        return {
            age,
            state,
            readOnlyAge
        }
    }
});

3. Responsive system

vue2 uses defineProperty to hijack the entire object, then deeply traverses all properties, adding getter and setter, implement responsiveness

vue3 uses proxy to rewrite the responsive system. Because proxy can monitor the entire object, no deep traversal is required.

  • Can monitor the addition of dynamic attributes
  • You can monitor the index of the array and the length property of the array
  • Can listen for deletion of attributes

Regarding the specific differences between these two APIs, our next article will give a more detailed introduction.

References

  • https://juejin.cn/post/6903171037211557895
syntaxbug.com © 2021 All Rights Reserved.