浅析vue3在源码、性能和语法上对比vue2做了哪些优化

Vue.js 从 1.x 到 2.0 版本,最大的升级就是引入了虚拟 DOM 的概念,它为后续做服务端渲染以及跨端框架 Weex 提供了基础。

Vue.js 2.x 发展了很久,现在周边的生态设施都已经非常完善了,而且对于 Vue.js 用户而言,它几乎满足了我们日常开发的所有需求。你可能觉得 Vue.js 2.x 已经足够优秀,但是在 Vue.js 作者尤小右的眼中它还不够完美。

在迭代 2.x 版本的过程中,小右发现了很多需要解决的痛点,比如源码自身的维护性,数据量大后带来的渲染和更新的性能问题,一些想舍弃但为了兼容一直保留的鸡肋 API 等;

另外,小右还希望能给开发人员带来更好的编程体验,比如更好的 TypeScript 支持、更好的逻辑复用实践等,所以他希望能从源码、性能和语法 API 三个大的方面优化框架。

那么接下来,我们就一起来看一下 Vue.js 3.0 具体做了哪些优化。相信你学习完这篇文章,不仅能知道 Vue.js 3.0 的升级给我们开发带来的收益,还能学习到一些设计思想和理念,并在自己的开发工作中应用,获得提升。

1、更好的代码管理方式:monorepo

monorepo是一种管理代码的方式,它的核心观点是所有的项目在一个代码仓库中,但是代码分割到一个个小的模块中,而不是都放在src这个目录下面。这样的分割,每个开发者大部分时只是工作在少数的几个文件夹以内的,并且也只会编译自己负责的模块,而且不会导致一个 IDE 打不开太大的项目之类的事情,这样很多事情就简单了很多。如下图:

monorepo是一种管理代码的方式,它的核心观点是所有的项目在一个代码仓库中,但是代码分割到一个个小的模块中,而不是都放在 src这个目录下面。这样的分割,每个开发者大部分时只是工作在少数的几个文件夹以内的,并且也只会编译自己负责的模块,而且不会导致一个 IDE 打不开太大的项目之类的事情,这样很多事情就简单了很多。如下图:
作者:吕小鸣
链接:https://www.imooc.com/article/310195
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢

首先源码的优化体现在代码的管理方式上。vue2.x的源码托管在src目录中,然后依据功能拆分出了complier(模板编译的相关代码),core(与平台无关的通用运行时代码),platforms(平台专有代码),server(服务端渲染的相关代码)、sfc(.vue 单文件解析相关代码)、shared(共享工具代码) 等目录。

而到了vue3.0,整个源码是通过 monorepo 的方式维护的,根据功能不同的模块拆分到packages目录下面不同的子目录中

可以看出相对于vue2.x的源码组织方式,monorepo把这些不同的package中,每一个package有各自的api,类型定义和测试,这样使得模块拆分更加细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性。

另外一点package可以独立于vue.js去使用,这样例如用户想要使用vue3.0的响应式,可以单独依赖reactive,而不必依赖整个vue.js,减少引用包的体积,而vue2.x却做不到这一点

monorepo是一种管理代码的方式,它的核心观点是所有的项目在一个代码仓库中,但是代码分割到一个个小的模块中,而不是都放在 src这个目录下面。这样的分割,每个开发者大部分时只是工作在少数的几个文件夹以内的,并且也只会编译自己负责的模块,而且不会导致一个 IDE 打不开太大的项目之类的事情,这样很多事情就简单了很多。如下图:
作者:吕小鸣
链接:https://www.imooc.com/article/310195
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作

2、使用 TypeScript 类型支持

在vue1.x中当时没有采用类型语言。但是对于开发大型框架的时候,使用类型语言非常有利于IDE对于类型推导。

所以尤雨溪在vue2.x使用的是Flow来进行开发,Flow是facebook出品的javascript的静态类型检查工具,但是flow对于一些复杂的场景flow支持的不是很好。

所以在vue3.x中vue全面转向typescript,typescript提供了更好的类型检查,也支持复杂的类型推导。

1、源码体积优化:移除冷门api、引入tree-shaking实行按需编译

在性能优化方面我们首先想到的是代码的体积,因为 JS 包的体积越小,意味着网络传输的时间就越短,JS 引擎解析包的速度也就越快。vue3.0在源码体积上做了哪些工作呢?

(1)首先,移除了一些冷门的api(例如:filter、inline-template)等

(2)其次,引入 tree-shaking 的技术来减少打包的体积

第一点非常容易理解,但是第二点该如何去理解呢?我们知道 webpack 打包时是有摇树优化的,会把一些未使用的函数编辑为 unused,然后在压缩时就会去除这些标记的代码,减少代码包体积。

在vue3.x中也是同样的道理,利用 tree-shaking 技术,如果项目中没有使用 transitionkeep-alive等组件时,那么这些代码就不会进行打包,这样就间接达到了减少项目引入vue.js包体积的目的。

2、数据劫持优化 proxy

响应式是vue.js和react的主要差别,从vue1.x版本就一直伴随着。DOM是数据的一种映射,数据发生变化后就会自动更新DOM,用户只需要专注于数据的修改,没有其余的心智负担。

在vue.js内部如果想要实现响应式,则就会出现数据的劫持和更新,也就是当数据发生改变时,会自定执行一些代码去更新DOM

在vue1.x和vue2.x内部是通过 Object.defineProperty这个 API 来获取数据的 gettersetter,但是这个api只能检测get和set,不能检测给对象增加属性和删除属性。此时vue为了解决这个问题,出现了 $set$delete实例方法。另外还存在一个问题,就是如果存在多个对象进行嵌套问题。例如:

由于vue.js无法判断你在运行时到底会访问那个属性,所以对于这样一个嵌套比较深的对象,就需要使用遍历吗,这无疑给性能增加了很大的负担。

为了解决如上问题,在vue3.0中使用来proxy api做数据劫持。由于proxy劫持的是整个对象,那么对于对象的增加删除都是可以劫持到的。

但是注意的是,Proxy api并不能检测到内部对象的变化,所以vue3.0的处理方式为在getter中去使用响应式递归,当真正需要响应式递归时,才会去递归,这样极大程度提升了性能问题

3、编译优化:diff 算法优化

先说结论:vue2中diff算法是整体全量比对,对整颗vdom树进行循环对比操作;而在vue3中加入了静态标签(PatchFlag)的概念,PatchFlag的意思是给元素上增加一个标记。

为了方便理解,下面为一张图

这是vue.js2.x从new Vue开始渲染DOM的流程,之前我们通过数据劫持来进行优化,下面我们可以对耗时较多的patch阶段进行优化。我们都知道vuejs2.x的数据更新并触发重新渲染的颗粒是组件级别的。

虽然vue能保证呢触发更新的组件最小化,但是单个组件内部仍然需要遍历该组件的整个vnode树,举一个例子,例如我们需要更新如下的组件。

整个的diff算法是这样的:

可以看到,因为代码中只有一个动态节点,所以这里有很多的diff的遍历其实都是不需要的,也就是说导致vnode性能的跟模板大小正相关,跟动态节点的数量无关。当一些组件的整个模板中只有少量的动态节点时,这些遍历都是性能的浪费。

对于上述例子中, 理想状态只需要diff这个绑定message的p标签即可。vue3.0做到了,通过对编译阶段对静态模板进行分析,编译生成 Block tree。借助 Block tree,Vue.js 将 vnode 更新性能由与模版整体大小相关提升为与动态内容的数量相关,这是一个非常大的性能突破。(这就是我们常常听说的那个静态标记了)

4、编译优化:PatchFlag(静态标记)、hoistStatic(静态提升)与渲染复用

Vue 2.x 中的虚拟 DOM 是全量对比的模式,而到了 Vue 3.0 开始,新增了静态标记(PatchFlag)。

在更新前的节点进行对比的时候,只会去对比带有静态标记的节点。并且 PatchFlag 枚举定义了十几种类型,用以更精确的定位需要对比节点的类型。

在vue2中,无论元素是否参与更新,在生成vdom树时都会重新创建该元素,而在vue3里有一种静态提升的概念,这个概念的核心思想是对于PatchFlag标记出的,不参与更新的元素提出来,只需创建一次,后续渲染时直接复用就行

从上面我们就可以看到 hostname 的 div 标签就是标记的 HOISTED -1 即静态标签,不会被 diff 的

5、cacheHandler 事件监听缓存

什么意思呢?在Vue2.0中,针对绑定事件@click=”onClick”,每次触发都要重新生成全新的 function 更新onClick

默认情况下@click会被视为动态绑定,所以每次都会去追踪它的变化,但是因为是同一个函数,所以没必要去追踪它的变化,想办法将它直接缓存起来复用就可以提升性能。

所以 Vue 3.0 对此作出了相应的优化叫事件监听缓存:在Vue3.0中,提供了事件缓存对象cacheHandlers,当cacheHandlers开启的时候,编译会自动生成一个内联函数,将其变成一个静态节点,当事件再次触发时,就无需重新创建函数直接调用缓存的事件回调方法即可。

在未开启事件监听缓存的情况下,我们看到这串代码编译后被静态标记为 8,之前讲解过被静态标记的标签就会被拉去做比较,而静态标记 8 对应的是”动态属性,不包括类名和样式”。 @click 被认为是动态属性,所以我们需要开启 Options 下的 cacheHandler 属性,如下图所示:

可以发现,开启 cacheHandler 之后,编译后的代码已经没有静态标记(PatchFlag),也就表明图中 P 标签不再被追踪比较变化,进而提升了 Vue 的性能

总结:一般为一个节点设置了监听事件,每次页面进行更新,就会重新生成新的监听函数。启用了cacheHandlers,就会在第一次更新的时候进行自动识别是否可以缓存,如果可以就进行缓存,这样页面更新就不需要重新生成,尤其是在组件上,极大地减少了子组件的不必要刷新和资源消耗。

6、编译优化:Fragment

模板内不用再创建一个唯一根节点,可以直接放同级标签和内容。就相当于少了一个节点嵌套渲染。

1、优化逻辑组织:Composition API

在vue1.x和vue2.x时,我们使用的是Options api,但是在vue3.x时,我们使用的是Componsition api。因为在vue2.x时,使用Options api如果处于比较小型的项目中,可能逻辑还是可以分清的,但是如果处于大型的项目中就需要上下来回的切换代码。

Componsition api 除了在逻辑复用方面存在一些优势,也会有更好的类型支持,因为它们都是一些函数,在调用函数时,自然所有的类型就被推导出来了,不像 Options API 所有的东西使用 this。

另外,Composition API 对 tree-shaking 友好,代码也更容易压缩。这里还需要说明的是,Composition API 属于 API 的增强,它并不是 Vue.js 3.0 组件开发的范式,如果你的组件足够简单,你还是可以使用 Options API。

总结下4点:不用来回切换代码、更好的逻辑复用、更好的类型支持,调用时类型直接推导出来、对 tree-shaking 友好

2、优化逻辑复用:use hooks

当我们开发的项目比较复杂的时候,免不了需要抽离出来一些复用的业务逻辑,在vue2.x中我们通常会使用混入(mixins)去复用逻辑。使用单个 mixin 问题不大,但是当我们一个组件混入大量不同的 mixins 的时候,会存在两个非常明显的问题:(1)命名冲突;(2)数据来源不清晰。

首先每个 mixin 都可以定义自己的 props、data,它们之间是无感的,所以很容易定义相同的变量,导致命名冲突。

另外对组件而言,如果模板中使用不在当前组件中定义的变量,那么就会不太容易知道这些变量在哪里定义的,这就是数据来源不清晰。

但是 Vue.js 3.0 设计的 Composition API,就很好地帮助我们解决了 mixins 的这两个问题。

解决方案:在 vue.js 3.x 中使用 hooks 来解决

使用 hooks,数据来源变得清晰了,即使去编写更多的hooks,也不会出现命名冲突的问题。

作为一个流行开源框架的作者,尤雨溪可能每天都受到很多的 feature request。但是并不是社区一存在新的功能的需求,框架就会马上实现,因为随着 Vue.js 的用户越来越多,小右会更加重视稳定性,会仔细考虑所做的每一个可能对最终用户影响的更改,以及有意识去防止新 API 对框架本身实现带来的复杂性的提升。

因此 vuejs2.x 版本开发到后期的阶段,尤雨溪就启动了RFC,他的全称为Request for comments。当社区有一些新需求的想法时,它可以提交一个 RFC,然后由社区和 Vue.js 的核心团队一起讨论,如果这个 RFC 最终被通过了,那么它才会被实现。

到了vuejs3.0实现代码前就大规模启用 RFC,来确保他的改动和设计都是经过讨论并确认的,这样可以避免走弯路。Vue.js 3.0 版本有很多重大的改动,每一条改动都会有对应的 RFC,通过阅读这些 RFC,你可以了解每一个 feature 采用或被废弃掉的前因后果。

个人学习总结,主要参考文章:https://blog.csdn.net/weixin_47450807/article/details/123478024

Original: https://www.cnblogs.com/goloving/p/16250256.html
Author: 古兰精
Title: 浅析vue3在源码、性能和语法上对比vue2做了哪些优化

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/547513/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球