1, Compilation phase
Reviewing Vue2, we know that each component instance corresponds to one watcher Instance, which records the data properties used during component rendering as dependencies. When the dependency changes and the setter is triggered, it will notify the watcher, so as to re render the associated components
Imagine a component structure as shown in the figure 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>
You can see that there is only one dynamic node inside the component, and the rest are static nodes, so there are many here diff And traversal are actually unnecessary, resulting in a waste of performance
Therefore, Vue3 is further optimized in the compilation phase. The main are as follows:
-
diff algorithm optimization
-
Static lift
-
Event listening cache
-
SSR optimization
diff algorithm optimization
vue3 adds static tags in diff algorithm compared with vue2
The function of this static tag is to add a flag tag to the place where changes will occur, and directly find the place for comparison when changes occur next time
In the following figure, the p tags of static nodes are not compared in the diff process, which further improves the performance
The static types are enumerated as follows
export const enum PatchFlags { TEXT = 1,// Dynamic text node CLASS = 1 << 1, // two Dynamic class STYLE = 1 << 2, // four Dynamic style PROPS = 1 << 3, // eight Dynamic properties, excluding class names and styles FULL_PROPS = 1 << 4, // sixteen dynamic Key, when key Changes require complete diff Algorithm comparison HYDRATE_EVENTS = 1 << 5, // thirty-two Represents a node with an event listener STABLE_FRAGMENT = 1 << 6, // sixty-four A that does not change the order of child nodes Fragment KEYED_FRAGMENT = 1 << 7, // one hundred and twenty-eight have key Attribute Fragment UNKEYED_FRAGMENT = 1 << 8, // two hundred and fifty-six No child nodes key of Fragment NEED_PATCH = 1 << 9, // 512 DYNAMIC_SLOTS = 1 << 10, // dynamic solt HOISTED = -1, // The special flag is a negative integer that will never be used as diff BAIL = -2 // A special flag refers to the difference algorithm }
Static lift
In Vue3, elements that do not participate in updating will be statically promoted, created only once, and reused directly during rendering
In this way, repeated node creation is avoided. Large applications will benefit from this change, eliminating repeated creation operations and optimizing the memory occupation at run time
<span>Hello</span> <div>{{ message }}</div>
Before static lifting
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 static lifting
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
Static content_ hoisted_1 is placed in render Function, each time you render, you just take _ hoisted_ one that will do
meanwhile _ hoisted_ one Got hit PatchFlag , The static flag 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 considered as dynamic binding, so it will track its changes every time
<div> <button @click = 'onClick'>Point me</button> </div>
Event listener cache 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 }, "Point me", 8 /* PROPS */, ["onClick"]) // PROPS=1<<3,// eight // Dynamic properties, but not class names and styles ])) })
After the event listener cache is turned on
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))) }, "Point me") ])) }
The above findings show that after the cache is turned on, there is no static tag. That is to say, the diff algorithm will be used directly next time
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 nodes will be directly innerHtml, so there is no need to create objects, and then render according to the objects
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 volume
Compared with Vue2, the overall volume of Vue3 is smaller. In addition to removing some infrequent API s, the most important thing is Tree shanking
Any function, such as ref, reactivated, computed, etc., is packaged only when it is used. Unused modules are shaken off, and the overall volume of packaging 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
Adopted in vue2 Define property to hijack the whole object, then deeply traverse all properties, add getter s and setter s to each property, and realize the response
vue3 uses proxy to rewrite the responsive system. Because proxy can listen to the whole object, it does not need deep traversal
-
You can listen for the addition of dynamic attributes
-
You can listen to the index of the array and the length attribute of the array
-
You can listen and delete attributes
The specific differences between the two API s will be described in more detail in the next article