Vue3比Vue2快在哪里?
宁静致远 7/18/2020 vue3
- Proxy 响应式
- PatchFlag
- hoistStatic
- cacheHandel
- SSR 优化
- tree-shaking
# Proxy 响应式
- 深度监听、性能更好
- 可监听新增/删除属性
- 可监听数组变化
- Proxy 能规避 Object.defineProperty 的问题
- Proxy 无法兼容所有浏览器,无法监听 Polyfill
# PatchFlag 动态标记
(vue2是全量比较)
- 编译模板时,动态节点做标记
- 标记,分为不同的类型
- diff 算法时,可以区分静态节点,以及不同类型的动态节点
- 静态提升
- Vue2中无论元素是否参与更新,每次都会重新创建,然后在渲染。
- Vue3中对于不参加更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可
export const enum PatchFlags {
TEXT = 1,// 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 表示只需要non-props修补的元素 (non-props不知道怎么翻才恰当~)
DYNAMIC_SLOTS = 1 << 10, // 1024 动态的solt
DEV_ROOT_FRAGMENT = 1 << 11, //2048 表示仅因为用户在模板的根级别放置注释而创建的片段。 这是一个仅用于开发的标志,因为注释在生产中被剥离。
//以下两个是特殊标记
HOISTED = -1, // 表示已提升的静态vnode,更新时调过整个子树
BAIL = -2 // 指示差异算法应该退出优化模式
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# hoistStatic ( HOISTED = -1,)
- 将静态节点的定义,提升到父作用域,缓存起来
- 多个相邻的静态节点,会被合并
- 拿空间换时间的优化策略
// 将静态节点的定义,提升到父作用域,缓存起来 (三个相邻节点)
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
HOISTED = -1,
import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, "Hello World", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "Hello World", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createElementVNode("div", null, "Hello World", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_hoisted_1,
_hoisted_2,
_hoisted_3
], 64 /* STABLE_FRAGMENT */))
}
// Check the console for the AST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 多个相邻的静态节点,会被合并 (10个相邻节点)
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
import { createElementVNode as _createElementVNode, createStaticVNode as _createStaticVNode } from "vue"
const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div>", 10)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return _hoisted_1
}
// Check the console for the AST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# cacheHandel 缓存事件
<div @click="handleClick">Hello World</div>
import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", {
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.handleClick && _ctx.handleClick(...args)))
}, "Hello World"))
}
// Check the console for the AST
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# SSR 优化
- 静态节点直接输出,绕过了vdom
- 动态节点,需要动态渲染
// 渲染之前,静态节点提前优化,直接输出
<div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<span>{{msg}}</span>
</div>
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>Hello World</div><div>Hello World</div><div>Hello World</div><span>${
_ssrInterpolate(_ctx.msg)
}</span></div>`)
}
// Check the console for the AST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# tree-shaking
- 编译时,根据不同情况,引入不同API,通过清除多于代码方式来优化项目打包体积(去除无用的代码)
- 借助ES6模块的静态编译思想
- 编译阶段,判断哪些模块已经加载
- 判断哪些模块和变量未被使用或者引用,进而删除对应代码
- 减少程序体积
- 减少程序执行时间
- 便于将来对程序架构进行优化