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 for each property. code>, 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