从源码设计看 Vue3 的性能提升

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

Vue.js 3.0

接下来我们接着 上一节学习 Vue3.0, 我们会通过以下几个小点,来对大家继续进行介绍

  • 源码组织方式的变化
  • Composition API
  • 性能提升
  • Vite

性能提升

Vue3 中的性能提升可以通过下面几个方面来说

  • 响应式系统升级
  • 编译优化
  • 源码体积的优化

响应式系统升级

  • Vue2.x 中响应式系统的核心 defineProperty
    • 在初始化的时候地柜每一个属性通过 definePropertygetset 对对象的属性进行转换
    • 划重点,在初始化的时候进行的,也就是在你没有使用这个属性的时候,Vue也对这个属性进行了处理
  • Vue3 中使用了 Proxy 对象重写了响应式系统
    • Proxy 对象的性能本身就比 defineProperty 要好
    • 代理对象可以拦截属性的访问赋值删除等操作
    • 不需要初始化的时候遍历每一个属性
    • 多层属性嵌套的时候,只有访问了,该层属性才会对该属性进行处理
    • 可以监听到动态新增的属性、删除的属性、甚至还可以监听数字索引的修改和length属性的修改

编译优化

这里我们通过一个组件来回顾一下,Vue2中对组件执行编译的过程

Vue2

  • 我们知道 Vue2 中模板需要先编译成 render 函数,这个过程一般是在构建的过程中完成的,在编译的时候会编译静态根节点、静态节点
  • 静态根节点中要求节点中必须有一个静态子节点,因此Vue2中会通过标记根节点的操作,优化diff过程,但是静态节点还需要被diff,这个过程没有被优化
  • 当组件的状态发生变化后,会通知 watcher 触发 update 函数,最终执行虚拟dom的patch操作,遍历所有的虚拟节点,找到差异更新到真实节点上
  • diff的过程中会去比较整个虚拟dom,先对比新旧的div以及他的属性,然后再对比他的子节点
  • Vue2中渲染的最小单位是组件

Vue3

1. Fragments片段的特性

这里我们来看 Vue-template-explorer(Vue3 在线编译)

Vue2 在线编译

1649854912(1).png 这里我们删除掉最外层的静态节点

image.png 可以看见

_createElementBlock('div', null, []) => _createElementBlock(_Fragment, null, [])
复制代码

代码中创建了_Fragment也就是我们之前说的片段,其实从这里可以看见,它最外层中还是维护了一个树形结构的

2. Vue3中标记和提升所有的静态节点,diff的时候只需要对比动态节点内容

接着我们打开 提升静态内容的选项

image.png

image.png 我们可以看见,几个静态节点的dom全部都被提取到了最外层,这些被提升的静态节点,只有在创建的时候被创建一次,之后我们在调用render的时候,只需要进行复用就好。

3.动态节点的优化

我们在来看模板中的插值表达式

<div>
    {{ count }}
</div>
复制代码

我们再来看一下对应的 render 的代码

_createElementVNode("div", null, _toDisplayString(_ctx.count), 1 /* TEXT */),
复制代码

我们可以看见最后的部分有一个 11 /* TEXT */表示文本内容动态绑定 ) 这是 patch_falg 标记,将来我们执行diff的时候,会执行有 patch_falg 标记的节点

我们来给他加上一个新的属性

 <div :id="id">
    {{ count }}
  </div>
复制代码

这时候我们来重新观察 render 中的代码

_createElementVNode("div", { id: _ctx.id }, _toDisplayString(_ctx.count), 9 /* TEXT, PROPS */, _hoisted_4),
复制代码

1 /* TEXT */ 变成了 9 /* TEXT, PROPS */, _hoisted_4,之后我们进行编译的时候只需要根据节点就可以很快的进行动态节点的编译

4.事件的优化

<button @click="handler">button</button>

_createElementVNode("button", { onClick: _ctx.handler }, "button", 8 /* PROPS */, _hoisted_5),
复制代码

这里通过 onClick 为该属性添加了一个事件处理函数,接着我们开启缓存操作

image.png

<button @click="handler">button</button>

 _createElementVNode("button", {
      onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.handler && _ctx.handler(...args)))
    }, "button"),
复制代码

这里的render代码发生了变化,当我们首次渲染的时候,他会去生成一个函数 (...args) => (_ctx.handler && _ctx.handler(...args)) 并且将函数赋值到 handler中,并且我们之后再次进行调用的时候,就会通过缓存中获取函数。避免不必要的更新

源码体积的优化

  • Vue3中移除了一些不常用的API
    • inline-template、filter(我倒是很喜欢用这个啊啊啊啊啊)等
  • 对 Tree-shaking 的依赖更好,按需引入做的更好。

下一章

下一章 我们会介绍 Vite 大家准备准备?一起学习学习?

猜你喜欢

转载自juejin.im/post/7086084262423691294
今日推荐