vue3的性能提升

1. diff 算法的优化

增加了静态标记, 被标记为-1的节点不会参与diff比较。

因为为vue3可以实现精准定位到动态节点,即那些可能需要重新渲染的节点。

曾经diff需要一层层比较整个新旧虚拟dom树找到哪些值改变。

  • 现在vue3在编译时,利用静态标记patchFlag 标记节点的动态属性。

例如class、style、props,含有动态属性的节点就是动态节点。

  • 每个组件的顶层vnode会有一个dynamicChildren属性,以数组形式存放该节点下所有动态节点。

  • 在运行render后,会遍历这个数组进行靶向更新。也就是说只需要比较动态节点的新旧变化了

2. 静态提升

对于静态节点,把创建dom节点的放在render函数之外。这样就只用创建一次,多次复用。

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

3. 事件监听缓存

用一个cache缓存事件处理函数

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)))
}, "点我")
]))
}

4. Tree shaking

tree shaking 是一种清除多于代码的优化打包体积的技术。

  • vue2中所有功能代码都被打包,因为vue2是单例模式,所有方法都在vue构造函数生成的单例上,treeshaking无法区分某个属性、方法是否用到。
  • vue3利用ES6 module。 静态编译时就可以把未使用的功能排除

5. 响应式系统

  • vue2 使用 defineProperty 来劫持对象,深度遍历所有属性给每个属性添加 getter setter
  • vue3 使用 Proxy 代理对象。
    • defineProperty 只能劫持对象属性,所以需要遍历所有属性。而Proxy是直接代理对象。
    • vue2由于defineProperty 而不能监听数组,所以要重写数组方法。
    • 当一个对象的属性还是一个对象时,Proxy 采用懒递归,在getter发现读到的属性时对象时才会创建该对象的代理并返回。