likes
comments
collection
share

vue3的全面提升

作者站长头像
站长
· 阅读数 16

前言: vue框架是采用MVVM的架构进行开发的,M就是model,就是数据层,进行数据的逻辑判断和执行,V是指View,指的是视图层,VM指的是数据的响应和渲染,是连接数据层和视图层的桥梁,可以理解为vue的响应式系统,vue通过观察者模式和采用代理的方式进行数据劫持和数据响应,实现了响应式系统,通过VNode,diff算法,还有对于一些事件的特殊定制,比如特制的指令操作和绑定操作,组件化处理,进行模版渲染操作,再和响应式系统、编译器联合,就形成了以数据驱动视图的声明式框架

问题来源

对于vue2来说,可以让开发者更好的上手进行vue的页面开发,但是这也带来了一些弊端,也是在对于熟练使用vue后,最让人难受的地方,它的捆绑模式太严重,导致为了调用某个值或者某个功能,让开发者进行一些冗余代码的编写,比如,函数的嵌套,this的滥用而导致一些逻辑错乱等等,这些问题在vue3的设计中,进行了全面的升级,它对于vue2是一次全新变革

架构的升级

vue3从架构层面来说,基于跨平台的优化,在渲染器、响应式、编译器、组件化,运行时等方面进行了拆分,使vue拥有跟好的跨平台性,首先,vue3对渲染器的优化,将渲染逻辑进行单独的封装,只要开发者将相关的js平台的渲染API按照格式传入渲染函数中,浏览器就会根据渲染逻辑进行时图的渲染,不在局限于dom的操作,像小程序,app等平台有了更好的兼容,其次,开发者可以单独进行响应式系统的使用,不像vue2中,响应式系统只是对vue2进行服务的,它的响应式系统,甚至可以进行React的代理响应

从setup函数说起

首先,vue3中采用在setup函数中进行代码的编写,这个函数可以更好的契合函数式的写法,相较于vue2的选项式options API的写法,函数式的写法更利于逻辑的梳理,不用像vue2一样,看一个逻辑反复跳动,而且,这种写法更像是一种编程式的写法,符合代码阅读和范式的一种规范性

然后,vue3的中将this进行undefinde的定义,通过单独调用API的方式,进行当前代码功能的定义,防止总是this形式的单一调用,避免像vue2中嵌套捆绑所导致的拓展性差和高耦合。

还有,对于功能复用,在vue2中,想要复用代码,需要通过minxs的方式进行混入,而函数式的写法,可以将一些公用的功能进行单独封装,不用minxs的方式,更加灵活

对于生命周期上,去掉了create和beforeCreate,实际是将其封装到setup函数中执行,在setup中进行了vue的实例化,并且对于mounted函数之类的钩子函数,可以进行多次调用,更好的理顺代码逻辑

vue3的全面提升

关于响应式系统

首先,vue2中采用的defindepropertype,其实是个伪代理,他本质上是通过对 对象 的属性修改的形式进行代理,这个有个缺点就是,对于代理的obj来说,如果没有代理的属性,definepropertype就不会进行拦截,可以这种做法,是一种懒代理,而vue3采用了proxy对象进行代理,在es6中进行语法上的定义,对obj对象,会进行全面的代理,不管是否有这个属性,都会同意拦截处理,像vue2中对obj对象的动态添加,或者是数组的动态添加或者是长度变化,在proxy中都得到了很好的解决,还增加了对于es6新增的数据类型(set,map等)的响应式代理功能

其次,响应式系统对于代理对象,才用了weakMap这种弱类型的数据结构,对于垃圾对象的回收和计算机内存的利用率都有明显的提升

还有对于computed和watch的功能,进行重新的定义,在computed中,通过缓存和调度器的方式,进行实现懒值的一个处理,在watch中,首先进行了首次渲染默认不执行以及首次执行的watchEffect的定义,通过参数进行watch的设定,包括回调函数的调度时机和watch的数量,并且返回的值可以控制watch的结束,他们内部都默认进行了响应式的处理

vue3的全面提升

对于组件的见的通讯,进行了emit和props的定义,都单独封装了API进行使用,包括对外暴露的功能,通过expose导出,增加了编码美感,移除了原来冗余的filters的功能

对于ts有了跟友好的支持,vue3框架就是用ts进行编写的,对于ts的支持是毋庸置疑的,另外,从写法上对于ts的支持,vue2有需要通过修饰器等辅助,vue3可以直接进行使用,比较方便,还有,对于vue2将实例的所有内容都绑定到this上的操作,其实,对于ts的类型定义和类型推导,是很麻烦的事情,vue3的拆分,反而更有利于类型推导和定义,从编码上,提高了代码的健壮性

渲染器优化

首先,patch函数的diff算法,采用快速diff算法,这个算法是通过预处理和寻找最长递增子序列的算法,将在原先的双端diff算法的基础上,提升了dom操作性能

什么是预处理,不做过度讨论,后面会进行详细的讲解 什么是最长递增子序列,不过度讨论,后面进行详细讲解,不过,源码中对于getSequence的实现如下:

在一个数组中获取最长递增子序列:

//core/packages/runtime-core/src/renderer.ts
function getSequence(arr: number[]): number[] {
  const p = arr.slice()
  const result = [0]
  let i, j, u, v, c
  const len = arr.length
  for (i = 0; i < len; i++) {
    const arrI = arr[i]
    if (arrI !== 0) {
      j = result[result.length - 1]
      if (arr[j] < arrI) {
        p[i] = j
        result.push(i)
        continue
      }
      u = 0
      v = result.length - 1
      while (u < v) {
        c = (u + v) >> 1
        if (arr[result[c]] < arrI) {
          u = c + 1
        } else {
          v = c
        }
      }
      if (arrI < arr[result[u]]) {
        if (u > 0) {
          p[i] = result[u - 1]
        }
        result[u] = i
      }
    }
  }
  u = result.length
  v = result[u - 1]
  while (u-- > 0) {
    result[u] = v
    v = p[v]
  }
  return result
}

采用fragment(片段)的方式,通过新增的fragment的元素类型处理方式,解决了在vue2中只能进行一个根节点的操作,提升了组件化的效率

新增了teleport内置组件和异步组件的方式,提升了在渲染方面的效率和多样化处理方式

对于v-if和v-for的不能并存问题,由原先的v-for的优先级大于v-if,改为了v-if大于v-for

编译器

 对于编译优化,通过编译在分析模版的过程中提取了关键字信息patchFlag,将其存放在编译好的vnode中,再将这些节点分成动态节点和静态节点,并根据动态节点生成一个block树,对于静态节点进行静态提升,还采用对静态节点预字符串化和v-once的方法,减少dom的创建和创建操作vnode的开销,以及缓存内联事件的方式,减少不必要组件的跟新

tree-sharing,由于vue3中的拆分,以及esm的方式导入和导出,可以在打包过程中未使用的代码进行清路,减小打包体积

总结:

基于vue3的升级,是编码风格进行规范化,写法上更范式,通过拆分的方式,我们可以像落积木的方式,进行代码功能的实现,代理也基于语法层面进行解决,还采用vite进行结合,更快的提高效率,但是,他也带来一些不好的地方,在与只能使用带有ESM的浏览器或者其他的平台,对于ESM的依赖性很大。