Vue.js框架演进的过程
Vue.js 1.x —》Vue.js 2.x
- 引入了虚拟Dom
- 为后续为服务端渲染,以及跨端框架weex提供了基础
Vue.js 2.x 作者发现的痛点
源码自身的维护性
=》源码
数据量大后带来的渲染和更新的性能问题
=》性能
兼容性,一些项舍弃但为了兼容一直保留的鸡肋API
=》语法
作者希望给开发人员带来的
- 更好的
编程体验
- 更好的
TypeScript支持
- 更好的
逻辑复用实践
- 更好的性能和更小的体积
Vue.js 3.0 主要做了哪些优化
一、源码优化
源码的优化主要体现在: 使用monorepo管理源码
和TypeScript开发源码
目的是为了让代码更易于开发和维护
更好的代码管理方式:monorepo
- 相对于Vue2.0的源码组织方式,monorepo把这些模块拆分为不同的package,每个package都有各自的
API
、类型定义
和测试
- 这样使模块拆分的更细,职责划分更明确,模块之间的依赖关系也更加明确
- 开发人员也更易阅读、理解和修改模块源码,提高代码的可维护性
- package(比如reactivity响应式库)是可以独立于Vue.js使用的
- 这样用户如果只想使用Vue.js 3.0的相应式能力,可单独依赖这个响应式而不用依赖整个Vue.js,减少了引用包的体积大小,而Vue.js 2.x 是做不到这一点的。
有类型的JavaScript:TypeScript
二、性能优化
移除一些冷门的feature
引入tree-shaking的技术
依赖ES2015模块语法的静态结构(即import和export)通过编译阶段的静态分析
,找到没有引入的模块并打上标记。然后压缩阶段会被压缩工具进行删除没有用到的代码
tree-shaking
三大打包工具的tree-shaking对于无用代码,无用模块的消除,都是有限的,有条件的。tree-shaking对函数效果较好,类消除实验中,rollup,webpack
全军覆没,都没有达到预期
如果你在项目中没有引入Transition、KeepAlive等组件,你么它们的代码就不会打包,这样也就间接达到了减少项目引入的Vue.js包体积的目的。
三、数据劫持优化
数据是响应式
Dom是数据的一种映射,数据变化后可以自动更新Dom,用户只需要专注于数据的修改,没有其余的心智负担。
如果要实现,必须劫持数据的访问和更新
当数据改变后,为了自动更新DOM,那么就必须劫持数据的更新
也就是说当数据发生改变后能自动执行一些代码去更新DOM
Vue.js怎么知道更新哪一片DOM呢?
因为在渲染DOM的时候访问了数据
,我们可以对它进行访问劫持 这样就在内部建立了依赖关系,也就知道数据对应的DOM是什么了
Vue2.x使用的是Object.defineProperty来实现响应式的缺陷
- 必选先知道需要拦截的Key是什么,所以并不能检测对象属性的添加和删除,尽管官方给出来$.set 和 $.delete的实例方法,但是还是增加了心智负担。
- 检讨层级比较深的对象,官方会进行层次遍历进行defineProperty,如果对象层级深,就会有较大的性能负担。
- 不能检测数组的变化,通过重写数组api来实现响应式
Vue3.x 使用了Proxy API 做数据劫持
- 因为检测的目标是对象,所以有效的解决了Object.defineProperty来实现响应式的缺陷
注意的是
,Proxy API 并不能监听到内部深层次的对象变化,因此Vue.js3.x的处理方式是在getter中区递归响应式
。这样的好处是
真正访问到内部对象才会变成响应式
,而不是无脑递归,这样无疑也在很大程度上提升了性能。
四、编译优化
vue.js2.x的渲染流程
- 上面说的响应式就是发生在init阶段
- 同时template 的compile render vnode阶段是可以在编译阶段离线完成,并非一定要在运行时完成。
- 所以想要优化整个vue运行时,处了
数据劫持部分的优化
,我们可以在耗时相对较多的patch阶段想办法
vue3.x通过在编译阶段优化编译的结果,实现运行时patch过程的优化
vue.js2.x的数据更新并触发重新渲染的粒度是组件级的:
虽然vuejs2.0能保证触发更新的组件最小化,但是在单个组件内部依然需要遍历该组件的vnode树,这样会导致vnode的性能和模板大小正相关,更动态节点的数量无关,当一些组件,模板只有少量动态节点时,这些遍历都是性能的浪费。
理想状态只需要diff 组件内动态修改的节点即可
vue3.x 通过编辑阶段对静态模版的分析,编译生成了Block tree
Brock tree
- Block tree 是一个将模板
基于动态节点
指令切割的嵌套区块,每个区块的内部结构苏固定的
,每个区块只需要以一个Array来追踪自身包含的动态节点 - 借助Block tree,Vue.js 将vnode更新性能由与模板整体大小相关提升为
与动态内容的数量相关
(这是一个特别大的性能突破)
Vue3.0在编译阶段 还包含了对Slot的编译优化
、事件监听函数的缓存优化
,并且在运行时重写了diff算法
五、语法API优化:Composition API
优化逻辑组织
Vue1.x 和Vue2.x 编写组件本质就是在编写一个 “包含了描述组件选项的对象”,我们把他成为Options API(好处在于十分符合直觉思维,对于新手来说容易上手和理解)
Options API
- Options API 的设计是按照
methods、computed、data、props
这些不同的选项分类 当组件小的时候
,这种分类方式一目了然;- 但是在大型组件中,一个组件可以有多个逻辑关注点,当使用Options API的时候,每一个关注点都有自己的Options,
如果需要修改一个逻辑点关注点
,就需要在单个文件中不断上下切换和寻找
新的API:Composition API
就是将摸个逻辑关注点相关的代码 全部放在一个函数里
这样当需要修改一个功能时,就不在需要再文件中跳来跳去
优化逻辑复用
Vue.js 2.x =》mixins去复用逻辑,当一个组件混入大量mixins时
- 没有mixins都是可以定义自己的props、data,它们直接是无感的,所以很容易定义相同的变量,导致
命令冲突
- 对于组件而言,
如果模板中使用不在当前组件中定义的变量
,那么就会不太容易知道这些变量在哪里定义的,这就是数据来源不清晰
Vue3.x Composition API
是类似hooks
的使用方式,所以天然的有逻辑复用方面
的优势,同时除了在 逻辑复用 方面有优势,也会有更好的类型支持
- 因为它们都是一些函数,在调用函数时,自然所以的类型就是被推到出来了,不像Options API 所以的东西使用 this
- 另外,Composition API 对 tree-shaking友好,代码也更容易压缩
- Vue3.x Composition API属于API的增强,并不属于Vue3.0的范式,当组件很简单时,也可以使用Options API。
五、引入RFC:是每个版本改动可控
- 大规模启用RFC( Request For Comments)
- 了解每一个feature采用或被废掉的前因后果