Vue3的优化

97 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

相比Vue2,Vue3在多方面做了优化,下面总结一下

1. 源码优化

源码优化面向的是V3框架的开发者,目的是为了让Vue.js框架本身的代码更易于开发和维护。主要体现在两个方面

  1. monorepo

v2的源码托管在src下,依据功能拆分出了 compier(编译相关),core(与平台无关的通用运行时代码),platforms(平台专有代码),server(服务端渲染代码),sfc(.vue文件解析相关代码)和shared(共享工具代码)等目录。而v3源码整体是通过monorepo的方式维护的,并根据功能将不同模块拆分到packages目录下的不同子目录中。模块拆分的粒度更细,职责划分更明确,模块之间依赖更明显,易读。

  1. TypeScript

抛弃Flow,全面拥抱ts

2. 性能优化

1. 源码体积优化

  1. 移除冷门功能,如filter、inline-template等,去掉了相关的代码
  2. 引入tree-shaking技术,减小打包体积。

原理:依赖Es6模块语法的静态结构(即import和export),通过编译阶段的静态分析,找到没有引入的模块并打上标记,然后压缩阶段删除已标记的代码。

2. 数据劫持优化

v2使用Object.defineProperty 这个API去劫持数据的setter和getter,但是这个API有一定的缺陷,它必须要先知道拦截的key是什么,所以并不能检测对象属性的添加和删除,尽管v2为了解决这个问题,提供了setset和delete函数,但这对于用户来说,无疑增加了一定的心智负担。

另外,对于嵌套层级较深的对象,vue无法判断你在运行时到底会访问哪个属性,如果要劫持它内部深层次的对象变化,就需要递归这个对象,执行Object.defineProperty 把每一层数据对象都变成响应式的。如此,对于响应式数据过于复杂,就会产生较大的性能负担。

v3为了解决上述问题,采用了Proxy 进行数据劫持,这个API劫持的是整个对象,因此对于属性的添加和删除都能检测到。需要注意的是,Proxy 这个API并不能侦听到内部深层次的对象变化,因此v3的处理方式是在Proxy处理对象的getter中递归响应。使之,只有真正访问到内部对象时,才会变成响应式的,而不是无脑递归。

3. 编译优化

在patch过程中优化,v2数据更新并触发重新渲染的粒度是组件级别的,v2虽然能保证触发更新的组件最小化,但是在单个组件内部仍需要遍历该组件的整个vnode树。比如我们要更新 这个组件:

<template>
  <div id="content">
    <p class="text">static text</p>
    <p class="text">static text</p>
    <p class="text">{{message}}</p>
    <p class="text">static text</p>
    <p class="text">static text</p>
  </div>
</template>

这个组件中只有一个动态节点,因此很多diff和遍历其实是不太需要的。但是在v2中会遍历所有的节点,这就会导致vnode的更新性能跟模块大小正相关,跟动态节点的数量无关,当组件的整个模版中只有少量的动态节点时,这些遍历就会造成性能的浪费。

对于上面例子,理想的状态是只需要diff这个绑定的message动态节点的p标签即可。

v3做到了,它通过对变异阶段对动态模块的分析,编译生成了Block Tree。Block Tree是将模版基于动态节点指令切割的嵌套区域,每个区块的内部节点结构是固定的,而且每个区块只需要以一个Array来追踪自身包含的动态节点。

借助Block Tree,vue3将vnode的更新性能由模版整体太小相关提升为与动态内容 的数量相关。这是一个非常大的突破。

3. 语法API优化

option api ---------> composition api

4. 优化逻辑复用

在v2中,通常使用mixin去复用逻辑,但是当一个组件混入大量的mixin的时候,会存在两个非常明显的问题:命名冲突,数据来源不清晰

v3的composition api很好的解决了这个问题,类似于react的hook

5. 引入RFC

全称request for comments,旨在为新功能进入框架提供一个一致且受控的路径,当社区有一些新需求的时候,首先会提交一个RFC,然后由社区和核心团队人员一起讨论,通过后,才会去被实现。