Vue3.x重点知识总结—理论篇(Vue3的优势)

一、Vue3相比与Vue2的优势

1. 速度更快、性能更好

1) 基于Proxy的新响应式系统(数据劫持优化)

Vue3.0如何实现响应式?

由原来的Object.definePropertygettersetter,改成了ES6 Proxy 作为其观察机制(准确说是 Proxy 配合 ReflectReflect提供了一些操作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将 compileruntime 结合(编译和运行相结合),对动态节点(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-templatefilter.sync$set等。

优化了打包方法,引入tree-shaking,按需编译,避免打包无用模块(例如只打包用到的ref,reactive,components等),使得打包后的bundle的体积更小,提升了运行效率。(通过摇树优化核⼼库体积,减少不必要的代码量)

另外,Composition APItree-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函数可以自定义渲染逻辑。我们可以自定义渲染的逻辑,甚至还可以渲染到canvaswebview等其他指定的平台。

5. 更易使用

响应式 Api 和 Hooks 直接暴露出来,哪里需要直接引用。比如 ref()toRefs()watch()等和生命周期钩子onMounted()onUpdated()onUnmounted()onBeforeMount()等。

6. 更好的TS支持

基于typescipt编写,可以享受到类型提示。对Ts支持更好。

编辑器可提供强有力的类型检查和错误提示,方便更好得调试。

二、Vue3相较于Vue2解决了哪些问题?设计目标是什么?

1. Vue3设计的初衷有两点(尤大)

  1. 利用新的语言特性(es6)
  2. 解决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 APIOptions API

通常使用Vue2开发的项目,普遍会存在以下问题:

  • 代码的可读性随着组件变大而变差,组件逻辑难以阅读和理解
  • 每一种代码复用的方式,都存在缺点
  • TypeScript支持有限

以上通过使用Composition Api都能迎刃而解,其两大显著的优化:

1)优化逻辑组织

Options API

包含⼀个描述组件选项(datamethodsprops等)的对象 options;开发时,同⼀个功能逻辑的代码会被拆分到不同选项中。

假设一个组件是一个大型组件,其内部有很多处理逻辑关注点,这种碎片化使得理解维护复杂组件变得困难。

选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块(比如在methodscomputed以及一些生命周期选项中反复横跳)。

⽤mixin重⽤公⽤代码,也有问题:命名冲突数据来源不清晰。(options选项不好拆分和重用)

Compostion API

基于函数的 api,可以更灵活的组织组件的逻辑。(一般为纯函数,没有副作用)

Composition ApiReact 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 两个属性,getset

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模板语法(importexports),主要是借助ES6模块的静态编译思想,在编译时就能确定模块的依赖关系,以及输入和输出的变量。

Tree shaking 无非就是做了两件事:

  • 编译阶段利用ES6 Module判断哪些模块已经加载
  • 判断哪些模块和变量未被使用,进而删除对应代码

3. 作用

通过 Tree shaking,Vue3 给我们带来的好处是:

  • 减少程序体积(更小)
  • 减少程序执行时间(更快)
  • 便于将来对程序架构进行优化(更友好)

七、都说 Composition API 和 React Hook 很像,请问他们的区别是什么?

React Hook 从实现的角度来看,React 是通过链表去实现 hooks 的调用的。需要确保每次更新时 hooks 的调用顺序一致,这让 React 能够在多次的 useStateuseEffect 调用之间保持 hook 状态的正确。

所以有以下几个限制:

  • 不在循环中、条件语句中、函数嵌套中调用 Hook
  • 你必须确保它总是在 React Top level 调用函数 Hook
  • 使用效果、依赖关系必须手动确定

Composition API 是基于 Vue 的响应系统,和React Hook 相比:

  • setup() 函数中,一个组件实例只执行一次,而React Hook 每次重新渲染时,都需要调用 Hook,给 React 带来的 GC 比 Vue 更大的压力,性能也相对 Vue 来说比较慢
  • Compositon API 不必担心调用的顺序,它也可以在循环中、条件、在嵌套函数中任意位置使用
  • 响应式系统自动实现依赖关系收集,而且组件的性能优化是由 Vue 内部完成的,而 React Hook 的依赖关系需要手动传递,并且依赖关系的顺序必须得到保证,尤其是使用 useEffectuseMemo 等 Hook 时,否则组件性能会因为依赖关系不正确而下降。
  • reactive + ref 属于响应式数据,⽐ react 的useState,要更难理解

虽然Compoliton API区别于React Hook,但它的设计思路也是来自React Hook的参考。

八、vue3.0有哪些改进(总结)

  1. 采用TS来编写,编辑器提供强有力的类型检查和错误提示,方便更好得调试。
  2. 支持composition API。
  3. 基于Proxy的响应式系统(由原来的Object.defineProperty的getter 和setter,改变成为了ES2015 Proxy 作为其观察机制),无需递归遍历数据,初始化效率更高,而且也可以监控数组。速度加倍,节省一半内存开销。但兼容性较差,不支持IE11。
  4. 虚拟 DOM 重写,提供更多的编译时提示来减少运行时开销。使用更有效的代码来创建虚拟节点。
  5. v-dom对比算法(diff算法)改写,更加细粒度,效率更高,比如将自定义组件和普通标签进行区分。只更新vdom绑定了动态数据的部分。
  6. v-dom静态属性缓存,避免重复patch。内存换时间。
  7. 虚拟dom编译优化,在compile阶段使用有 transform 对 AST (抽象语法树)做诸多层面的转化,使「Vue」 diff 过程更快。相比之下,「Vue2.x」的编译阶段没有完整的 transform,只是 optimize 优化了一下 AST。
  8. 静态提升、靶向更新等优化点,来提高 patchVNode (diff算法递归打补丁)过程。
  9. Composition API (结构化API)方便逻辑复用,代替原先options API(配置化)。
  10. 模块化更加解耦,可以单独引用某个模块而不用全部引入vue
  11. 2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用,而3.x版本中v-if优先级更高。
  12. 3.x版本使用:ref 绑定函数的方式取代$refs方式获取dom
  13. 组件的v-model重构。具体见文档。
  14. 总之就是:1. 更快 2. 更小 3. 更容易维护 4. 更加友好 5. 更容易使用
  15. vue3性能比vue2好的原因?1.diff算法优化;2.静态提升hoistStatic;3.事件侦听器缓存 cacheHandles
加载中...
加载中...