0%

Vue3性能提升的几个方面

编译阶段:

  • diff算法优化
  • 静态提升
  • 事件监听缓存
  • SSR优化

diff算法优化

vue3在diff算法中相比vue2增加了静态标记

作用是为了会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较。已经标记静态结点的p标签在diff过程中不会比较,把性能进一步提高

关于静态类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export const enum PatchFlags {
TEXT = 1,// 动态的文本节点
CLASS = 1 << 1, // 2 动态的 class
STYLE = 1 << 2, // 4 动态的 style
PROPS = 1 << 3, // 8 动态属性,不包括类名和样式
FULL_PROPS = 1 << 4, // 16 动态 key,当 key 变化时需要完整的 diff 算法做比较
HYDRATE_EVENTS = 1 << 5, // 32 表示带有事件监听器的节点
STABLE_FRAGMENT = 1 << 6, // 64 一个不会改变子节点顺序的 Fragment
KEYED_FRAGMENT = 1 << 7, // 128 带有 key 属性的 Fragment
UNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有 key 的 Fragment
NEED_PATCH = 1 << 9, // 512
DYNAMIC_SLOTS = 1 << 10, // 动态 solt
HOISTED = -1, // 特殊标志是负整数表示永远不会用作 diff
BAIL = -2 // 一个特殊的标志,指代差异算法
}

静态提升

Vue3中堆不参与更新得元素,会做静态提升,只会被创建一次,在渲染时直接复用,这样就免去了重复的创建节点,大型应用会受益于这个改动,免去重复的创建操作,优化了运行时候的内存占用

1
2
3
<span>你好</span>

<div>{{ message }}</div>

没有做静态提升之前

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

做了静态提升后

1
2
3
4
5
6
7
8
9
10
const _hoisted_1 = /*#__PURE__*/_createVNode("span", null, "你好", -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

静态内容_hosted_1被放置在render函数外,每次渲染的时候只要取_hosted_即可,同时_hosted_1被打上PatchFlag,静态标记为-1,特殊标记是负整数表示永远不会用于Diff

事件监听缓存

默认情况下绑定事件行为会被认为是动态绑定,所以每次都会去追踪它的变化

1
2
3
<div>
<button @click = 'onClick'>点我</button>
</div>

没开启事件监听器缓存

1
2
3
4
5
6
export const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("button", { onClick: _ctx.onClick }, "点我", 8 /* PROPS */, ["onClick"])
// PROPS=1<<3,// 8 //动态属性,但不包含类名和样式
]))
})

开启事件监听器缓存

1
2
3
4
5
6
7
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)))
}, "点我")
]))
}

开启缓存后,没有了静态标记,也就是说下次diff算法的时候直接使用

SSR优化

当静态内容大到一定量级,会用createStaticVNode方法在客户端生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,任何根据对象渲染

1
2
3
4
5
6
7
8
9
div>
<div>
<span>你好</span>
</div>
... // 很多个静态属性
<div>
<span>{{ message }}</span>
</div>
</div>

编译后

1
2
3
4
5
6
7
8
9
10
11
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>你好</span>...<div><span>你好</span><div><span>${
_ssrInterpolate(_ctx.message)
}</span></div></div>`)
}

源码体积

相比Vue2,Vue3整体体积变小,除了移除一些不常用API,最重要的是Tree shaking,任何一个函数,如ref,reactive,computed,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小

响应式系统

vue2采用的是defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter,setter,实现响应式,而vue3采用proxy重写响应式系统,因为proxy可以对整个对象进行监听,所有不需要深度遍历

  • 可以监听动态属性的添加
  • 可以监听到数组索引和数组length属性
  • 可以监听删除属性