一、Vue3相比与Vue2的优势
1. 速度更快、性能更好
1) 基于Proxy的新响应式系统(数据劫持优化)
Vue3.0如何实现响应式?
由原来的Object.defineProperty
的getter
和setter
,改成了ES6 Proxy
作为其观察机制(准确说是 Proxy
配合 Reflect
,Reflect
提供了一些操作Object
对象的方法),初始化时无需递归遍历数据,初始化效率更高,而且也可以监控数组。速度加倍,节省了一半内存开销。
Proxy
相当于在⽬标对象之前架设⼀层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了⼀种机制,可以对外界的访问进⾏过滤和改写。
Proxy
的性能本来⽐defineProperty
好,Proxy
可以拦截属性的访问、赋值、删除等操作,不需要初始化的时候遍历所有属性。另外有多层属性嵌套的话,只有访问某个属性的时候,才会递归处理下⼀级的属性。
- 可以劫持整个对象
- 可以监听对象动态属性的添加
- 可以监听到数组的变化,包括数组的索引和数组length属性
- 可以监听删除属性
- 操作时不是对原对象操作,
new Proxy
返回⼀个新对象(它不直接操作对象,而是代理模式,通过对象的代理对象进行操作)。
Vue2为啥不使用Proxy?答案是由于浏览器兼容性问题。目前来看,除不支持IE11及以下版本,其余浏览器均已支持。
2) 虚拟DOM重写
虚拟 DOM 重写,提供更多的编译时提示来减少运行时开销。使用更有效的代码来创建虚拟节点。
虚拟DOM静态属性缓存,避免重复patch。内存换时间。
3) Diff算法优化
算法改写,更加细粒度,效率更高。
增加了静态标记flag
。标记和提升所有静态根节点,diff 的时候只⽐较动态节点内容。
Patch flag
: 在动态标签末尾加上相应的标记,只能带 patchFlag 的节点才被认为是动态的元素,会被追踪属性的修改,能快速的找到动态节点,⽽不⽤逐个逐层遍历,提⾼了虚拟dom diff的性能。
将自定义组件和普通标签进行区分、将静态节点和动态节点进行区分。
4) 静态提升(hoistStatic),靶向更新,提升VNode更新性能
静态提升、靶向更新等优化点,来提高 patchVNode
(diff算法递归打补丁)性能。
静态提升:Vue2中无论元素是否参与更新,每次都会重新创建(createVNode
),然后再渲染(Vue2 中的虚拟dom节点是进⾏全量的更新)。但是在Vue3中使用了静态提升后,对于静态不需要发生变化的元素,只会被创建一次,静态节点都被提升到 render
⽅法之外,在渲染时直接复用即可。(静态提升避免了静态元素节点频繁重复创建)
靶向更新:Vue3将 compile
和 runtime
结合(编译和运行相结合),对动态节点(Block VNode
)进行精准标记。靶向更新的本质是为了从一颗存在动态、静态节点的 VNode Tree 中筛选出动态的节点形成 Block Tree,即 dynamicChildren
,然后在 patch 时实现精准、快速的更新。(Patch flag,跳过静态节点,直接对⽐动态节点)
5) SSR渲染优化
当有大量静态内容时,这些内容会被当成纯字符串推进一个buffer
里面,即使存在动态的绑定,会通过插值嵌入进去。这样会比通过虚拟dom来渲染快很多。当静态内容大到一定量级的时候,会用_createStaticVNode
方法在在客户端去生成一个static node
,这些静态node会被直接innerHTML
,就不需要创建对象,然后直接根据对象渲染。
6) 事件侦听器缓存(cacheHandles)
Vue3中的事件绑定的函数会被缓存起来,再次复用时就会提升性能。在patch过程中不会重新生成新的函数。
缓存事件处理函数cacheHandler,避免每次触发更新都要重新⽣成全新的function去更新之前的函数。
2. 体积更小
vue3整个源码体积相对减少,移出了一些不常用的Api,例如:inline-template
、filter
、.sync
、$set
等。
优化了打包方法,引入tree-shaking
,按需编译,避免打包无用模块(例如只打包用到的ref
,reactive
,components
等),使得打包后的bundle的体积更小,提升了运行效率。(通过摇树优化核⼼库体积,减少不必要的代码量)
另外,Composition API
对 tree-shaking
友好,代码也更容易压缩。(Tree shaking
详述)
3. 更容易维护
语法API方面(compositon Api
):
- 支持组合API(
compositon Api
),可与现有的Options API
一起使用 - 灵活的逻辑组织与逻辑复用
- Vue3模块可以和其他框架搭配使用
compositon Api
可以解决业务分离问题,使代码有更好的复用性。同时, 也方便后续的维护和管理,setup()
的出现,使得相关的业务代码得以集中起来,方便查找和维护。我们可以把不同的业务代码进行逻辑抽离,比如使用hooks形式,更容易维护。而Vue2不同的业务代码都混杂在options中,不便管理。
4. 更接近原生
compositon Api
使得 Vue 3 逻辑代码编写更趋向于原生。
可以自定义渲染 API:createRenderer
函数可以自定义渲染逻辑。我们可以自定义渲染的逻辑,甚至还可以渲染到canvas
,webview
等其他指定的平台。
5. 更易使用
响应式 Api 和 Hooks 直接暴露出来,哪里需要直接引用。比如 ref()
、 toRefs()
、 watch()
等和生命周期钩子onMounted()
、onUpdated()
、onUnmounted()
、onBeforeMount()
等。
6. 更好的TS支持
基于typescipt编写,可以享受到类型提示。对Ts支持更好。
编辑器可提供强有力的类型检查和错误提示,方便更好得调试。
二、Vue3相较于Vue2解决了哪些问题?设计目标是什么?
1. Vue3设计的初衷有两点(尤大)
- 利用新的语言特性(es6)
- 解决Vue3的一些设计和架构的问题
2. Vue2长期以来存在的一些问题
- 随着功能的增长,复杂组件的代码变得越来越难以维护
- 数据量大后带来的渲染和更新性能问题
- 缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制
- 类型推断不够友好
- bundle的时间太久了
3. Vue3的设计目标
- 更小
- 更快
- TypeScript支持
- API设计一致性
- 提高自身可维护性
- 开放更多底层功能
三、Vue3编译方面有哪些优势
Vue3更快主要体现在编译方面:
四、Vue3做了哪些优化方案
vue3从很多层面都做了优化,可以分成三个方面:
1. 源码
源码可以从两个层面展开:
1)源码管理
更好的代码管理方式: monorepo
(简单的说monorepo
模式就是把你组内的所有项目管理在一个git仓库中)。
monorepo
将不同的模块拆分到packages
目录下不同子目录中,每个package
有各自的API、类型定义和测试,这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性。每个魔抗可独立于Vue使用,减少引用包的体积大小。
2)TypeScript
- 编码期间可以进行类型检查,避免类型问题的错误
- 有利于定义接口类型,利于IDE对变量类型的推导
Composition API
几乎是函数,也会有更好的类型推断
2. 性能
vue3是从什么哪些方面对性能进行进一步优化呢?
3. 语法API(Composition API 的出现带来哪些新的开发体验,为啥需要这个? )
这里当然说的就是Composition API
,Vue3为什么要使用Composition API
取代Options API
呢?
如何看待Composition API
和Options API
?
通常使用Vue2开发的项目,普遍会存在以下问题:
- 代码的可读性随着组件变大而变差,组件逻辑难以阅读和理解
- 每一种代码复用的方式,都存在缺点
- TypeScript支持有限
以上通过使用Composition Api
都能迎刃而解,其两大显著的优化:
1)优化逻辑组织
Options API
包含⼀个描述组件选项(data
、methods
、props
等)的对象 options
;开发时,同⼀个功能逻辑的代码会被拆分到不同选项中。
假设一个组件是一个大型组件,其内部有很多处理逻辑关注点,这种碎片化使得理解和维护复杂组件变得困难。
选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块(比如在methods
和computed
以及一些生命周期选项中反复横跳)。
⽤mixin重⽤公⽤代码,也有问题:命名冲突,数据来源不清晰。(options选项不好拆分和重用)
Compostion API
基于函数的 api,可以更灵活的组织组件的逻辑。(一般为纯函数,没有副作用)
Composition Api
与React Hooks
有异曲同工之妙,是为了解决复杂业务逻辑而设计的。
将某个逻辑关注点相关的代码全都放在一个函数里(更加的高内聚,低耦合),这样当需要修改一个功能时,就不再需要在文件中或各个选项中来跳去。也方便使用hooks的方式做逻辑抽离。
Composition API
解决了几个问题:
- 根据业务逻辑组织代码,提⾼可读性和可维护性
- 解决在⽣命周期函数经常包含不相关的逻辑,但⼜不得不把相关逻辑分离到了⼏个不同⽅法中的问题。(如在
mounted
中设置定时器,但需要在beforeDestroy
中来清除) - 更好的重⽤逻辑代码,在Options API中通过MIxins重⽤逻辑代码,容易发⽣命名冲突,数据来源不清晰。
- 很少使用到
this
,减少了this
指向不明的情况 Composition API
都是函数,有更好的类型推导- 模块化更加解耦,可以单独引用某个模块而不用全量引入Vue对象
当然,Options API
并没有被废弃,与Composition API
可以共存,但并不推荐这样做(可能会引起混乱)。
小型项目,业务逻辑简单的可以使用Options API
,复杂的大型项目还是使用Composition API
更好。
2)优化逻辑的复用:mixins
使用单个mixins
没太大问题。但当组件中存在大量多个的mixins
后,会存在命名冲突和数据来源不清晰问题,组合式API解决了这两个问题。即使去编写更多的 hook 函数,也不会出现这两个问题。
五、Vue3里为什么要用 Proxy 替代 defineProperty ?
Object.defineProperty()
只能遍历对象属性进行劫持,Proxy
直接可以劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式目的,且可以解决 defineProperty
存在的问题。且提升性能。
Object.defineProperty()
此方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。文档
为什么能实现响应式:
通过 defineProperty
两个属性,get
及set
get:属性的 getter 函数,当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this
对象(由于继承关系,这里的this
并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值
set:属性的 setter 函数,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this
对象。默认为 undefined
存在弊端:
- 检测不到对象属性的添加和删除
- 数组API方法无法监听到
- 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题
Proxy 对象
对对象的所有操作进行监听。文档
这里需要对 ES6 对应的 Proxy
新特性进行复盘。
六、说说Vue 3.0中Treeshaking特性?举例说明一下?
1. 是什么
Tree shaking
是一种通过清除多余代码方式来优化项目打包体积的技术。
专业术语叫 Dead code elimination
(死码消除)。简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码。
在Vue2中,无论我们使用什么功能,它们最终都会出现在生产代码中。主要原因是Vue实例在项目中是单例的,捆绑程序无法检测到该对象的哪些属性在代码中被使用到。
而Vue3源码引入tree shaking
特性,将全局 API 进行分块。如果您不使用其某些功能,它们将不会包含在您的基础包中。
2. 原理
Tree shaking
是基于ES6模板语法(import
与 exports
),主要是借助ES6模块的静态编译思想,在编译时就能确定模块的依赖关系,以及输入和输出的变量。
Tree shaking
无非就是做了两件事:
- 编译阶段利用ES6 Module判断哪些模块已经加载
- 判断哪些模块和变量未被使用,进而删除对应代码
3. 作用
通过 Tree shaking
,Vue3 给我们带来的好处是:
- 减少程序体积(更小)
- 减少程序执行时间(更快)
- 便于将来对程序架构进行优化(更友好)
七、都说 Composition API 和 React Hook 很像,请问他们的区别是什么?
从 React Hook
从实现的角度来看,React 是通过链表去实现 hooks 的调用的。需要确保每次更新时 hooks 的调用顺序一致,这让 React 能够在多次的 useState
和 useEffect
调用之间保持 hook 状态的正确。
所以有以下几个限制:
- 不在循环中、条件语句中、函数嵌套中调用 Hook
- 你必须确保它总是在 React Top level 调用函数 Hook
- 使用效果、依赖关系必须手动确定
和 Composition API
是基于 Vue 的响应系统,和React Hook
相比:
- 在
setup()
函数中,一个组件实例只执行一次,而React Hook
每次重新渲染时,都需要调用 Hook,给 React 带来的 GC 比 Vue 更大的压力,性能也相对 Vue 来说比较慢 Compositon API
不必担心调用的顺序,它也可以在循环中、条件、在嵌套函数中任意位置使用- 响应式系统自动实现依赖关系收集,而且组件的性能优化是由 Vue 内部完成的,而
React Hook
的依赖关系需要手动传递,并且依赖关系的顺序必须得到保证,尤其是使用useEffect
、useMemo
等 Hook 时,否则组件性能会因为依赖关系不正确而下降。
reactive
+ref
属于响应式数据,⽐ react 的useState
,要更难理解
虽然Compoliton API
区别于React Hook
,但它的设计思路也是来自React Hook
的参考。
八、vue3.0有哪些改进(总结)
- 采用TS来编写,编辑器提供强有力的类型检查和错误提示,方便更好得调试。
- 支持composition API。
- 基于Proxy的响应式系统(由原来的
Object.defineProperty
的getter 和setter,改变成为了ES2015 Proxy 作为其观察机制),无需递归遍历数据,初始化效率更高,而且也可以监控数组。速度加倍,节省一半内存开销。但兼容性较差,不支持IE11。 - 虚拟 DOM 重写,提供更多的编译时提示来减少运行时开销。使用更有效的代码来创建虚拟节点。
- v-dom对比算法(diff算法)改写,更加细粒度,效率更高,比如将自定义组件和普通标签进行区分。只更新vdom绑定了动态数据的部分。
- v-dom静态属性缓存,避免重复patch。内存换时间。
- 虚拟dom编译优化,在compile阶段使用有 transform 对 AST (抽象语法树)做诸多层面的转化,使「Vue」 diff 过程更快。相比之下,「Vue2.x」的编译阶段没有完整的 transform,只是 optimize 优化了一下 AST。
- 静态提升、靶向更新等优化点,来提高
patchVNode
(diff算法递归打补丁)过程。 - Composition API (结构化API)方便逻辑复用,代替原先options API(配置化)。
- 模块化更加解耦,可以单独引用某个模块而不用全部引入vue
- 2.x 版本中在一个元素上同时使用
v-if
和v-for
时,v-for
会优先作用,而3.x版本中v-if优先级更高。 - 3.x版本使用
:ref
绑定函数的方式取代$refs
方式获取dom - 组件的
v-model
重构。具体见文档。 - 总之就是:1. 更快 2. 更小 3. 更容易维护 4. 更加友好 5. 更容易使用
- vue3性能比vue2好的原因?1.diff算法优化;2.静态提升hoistStatic;3.事件侦听器缓存 cacheHandles
本文固定连接:https://code.zuifengyun.com/2022/09/2632.html,转载须征得作者授权。