vue3为什么要比vue2快?

320 阅读2分钟

来自vue3的编译优化

1.PatchFlag静态节点标记处理

vue3相比于vue2, 添加了静态节点和动态节点的标记区分, 在进行diff算法时,vue3编译出的静态节点是不会进行diff比较的。 而vue2会去做同层静态节点中,text内容是否相同的比较。 vue3 diff更快,更高效。 vue3 编译代码如下:

  • html 模板内容
<div>
	<span>我是静态节点</span>
	<span>我是静态节点</span>
	<span>{{msg}}</span>
  <span :class="name">我是有属性的节点</span>
</div>
  • 模板编译
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, normalizeClass as _normalizeClass, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("span", null, "我是静态节点"),
    _createElementVNode("span", null, "我是静态节点"),
    _createElementVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
    _createElementVNode("span", {
      class: _normalizeClass(_ctx.name)
    }, "我是有属性的节点", 2 /* CLASS */)
  ]))
}

从以上代码片段可以看出,静态节点是不带第四个数值类型参数的。而diff可以根据PatchFlag这个标记过滤掉静态节点的比较运算,提升vue性能。

2.hoistStatic与 cacheHandler

  • hoistStatic是将静态节点的定义,做了变量提升,提升到父级作用域进行缓存。 且当出现多个相邻的静态节点时,达到一定阈值,这些静态节点将会被合并。是典型的用空间换时间的优化策略。
  • cacheHandler 是对事件函数进行缓存,如果缓存中有对应函数就直接调用,没有就入_cache,进行缓存。 代码片段如下:
<div>
	<span @click="onClickHandler">点击事件</span>
</div>
import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("span", {
      onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.onClickHandler && _ctx.onClickHandler(...args)))
    }, "点击事件")
  ]))
}

3.SSR和tree shaking优化

  • SSR优化:编译时提前判断。直接将静态节点直接输出成字符串,绕过v-dom逻辑。
  • tree shaking: 模板编译根据html标签,根据属性或者指令等,会动态引入当前标签需要用到的方法。可以理解为按需使用。

Proxy中的性能优化

相比于vue2,vue2 在data属性中定义的响应式数据,在进行设置对象深度响应式时,是一次性全部递归完成的。 且不能监听到新增和删除的属性的响应。 得通过 $set的方式设置。 且数组类型还得改写数组原型上的函数,再进行递归和监听。 而Proxy(代理)和Reflect(反射)就很好的解决了这个问题,进行深度监听时,只递归到访问到的属性这一层。 提供了deleteProperty API 以及set中 可以监听新增和删除的属性。 数组监听方面也不需要再额外去取数组原型方法。在功能和性能方面有明显的优化。