编译阶段:
- 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, STYLE = 1 << 2, PROPS = 1 << 3, FULL_PROPS = 1 << 4, HYDRATE_EVENTS = 1 << 5, STABLE_FRAGMENT = 1 << 6, KEYED_FRAGMENT = 1 << 7, UNKEYED_FRAGMENT = 1 << 8, NEED_PATCH = 1 << 9, DYNAMIC_SLOTS = 1 << 10, HOISTED = -1, 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 ) ], 64 )) }
|
做了静态提升后
1 2 3 4 5 6 7 8 9 10
| const _hoisted_1 = _createVNode("span", null, "你好", -1 )
export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock(_Fragment, null, [ _hoisted_1, _createVNode("div", null, _toDisplayString(_ctx.message), 1 ) ], 64 )) }
|
静态内容_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 = _withId(function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock("div", null, [ _createVNode("button", { onClick: _ctx.onClick }, "点我", 8 , ["onClick"]) ])) })
|
开启事件监听器缓存
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属性
- 可以监听删除属性