Vue3 新特性

一、vue3 为什么要重写

  两个主要原因考虑重写vue新版本主要功能:

  1.主流浏览器对新的JavaScript语言特性的普遍支持。

  2.当前Vue代码库随着时间的推移而暴露出来的设计和体系架构问题。

  3.对一些方法及API进行优化

  以下是一些原理上的分析:

  1.浏览器性能提升

  首先,随着ES6的发展已及广泛使用,浏览器对这些新的特性逐渐增加,性能不断优化,这就给vue3优化提供了一个机会,通过重写来优化提升vue的性能。

  2.底层实现方法

  其次,在框架设计上,vue2.0 是采用Object.defineProperty来实现双向绑定原理,这个属性本身就存在一些不足的地方,比如:

  1.Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 为了解决这个问题,经过vue内部处理后可以使用以下几种方法来监听数组,push(),pop(),shift(),unshift(),splice(),sort(),reverse();由于只针对了以上八种方法进行了hack处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。

  2.Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2.x里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择,新增的属性还行通过set方法来添加监听,有一定的局限性。

  vue3主要采用的Proxy特性,相比之下有以下优点:

  1.可以劫持整个对象,并返回一个新的对象

  2.有13种劫持操作

  但同时Proxy作为ES6的新特性,有一定的兼容问题,最主要的是这个属性无法用polyfill来兼容,这个需要在vue3中需要解决的问题。

  3.切换到TypeScript 

  Vue 2最初是用纯ES(Javascript)写成的。在原型设计阶段之后不久,我们意识到一个类型系统(Type system)对于这样一个规模的项目非常有用。类型检查(Type check)大大减少了在重构过程中引入意外错误的机会,并帮助贡献者更有信心进行大范围的更改。我们采用了Facebook的Flow type checker,因为它可以逐渐添加到现有的纯ES项目中。Flow type checker在一定程度上起到了帮助作用,但我们并没有从中得到我们所希望的那么多好处。特别是,持续的重大改变使得升级成为一种痛苦。相比较TypeScript与Visual Studio Code集成开发工具的深度集成,Flow type checker对集成开发环境的支持也不理想。

  我们还注意到,用户越来越多地同时使用Vue和TypeScript。为了支持它们的用例,我们必须独立于使用不同类型系统的源代码来编写和维护TypeScript声明。切换到TypeScript将允许我们自动生成声明文件,从而减轻维护负担。

  性能对前端框架至关重要。尽管Vue 2号称具有良好的性能,但重写提供了一个机会,可以通过试验新的渲染策略来更提供更好的性能。

  4.克服虚拟DOM的瓶颈 

  Vue有一个相当独特的渲染策略:它提供类似于HTML的模板语法,但是,它是将模板编译成渲染函数来返回虚拟DOM树。Vue框架通过递归遍历两个虚拟DOM树,并比较每个节点上的每个属性,来确定实际DOM的哪些部分需要更新。由于现代JavaScript引擎执行的高级优化,这种有点暴力的算法通常非常快速,但是DOM的更新仍然涉及许多不必要的CPU工作。当你看到一个基本上是静态内容、只有少量动态绑定的模板时,效率低下的情况尤其明显,因为这时候仍然需要递归地遍历整个虚拟DOM树,以找出需要更改的内容。

  幸运的是,模板编译步骤使我们有机会对模板执行静态分析并提取有关动态部分的信息。Vue 2在某种程度上是通过跳过静态子树来实现的,但是过于简单的编译器体系架构使得更高级的优化很难实现。在Vue 3中,我们使用适当的AST转换管道重写编译器,这允许我们以转换插件的形式将编译时(compile-time)优化组合进来。

  随着新的体系架构的出现,我们希望找到一种能够尽可能减少开销的渲染策略。一种选择是抛弃虚拟DOM并直接生成命令式DOM操作,但这样做会消除直接编写虚拟DOM渲染函数的能力,而我们发现这种能力对于高级用户和库的编写者非常有价值。另外,这将是一个巨大的突破性改变。另一个更好的办法是去掉不必要的虚拟DOM树遍历和属性比较,这在更新期间往往会产生最大的性能开销。为了实现这一点,编译器和运行时需要协同工作:编译器分析模板并生成带有优化提示的代码,而运行时尽可能获取提示并采用快速路径。这里有三个主要的优化:

  首先,在DOM树级别。我们注意到,在没有动态改变节点结构的模板指令(例如v-if和v-for)的情况下,节点结构保持完全静态。如果我们将一个模板分成由这些结构指令分隔的嵌套“块”,则每个块中的节点结构将再次完全静态。当我们更新块中的节点时,我们不再需要递归遍历DOM树 - 该块内的动态绑定可以在一个平面数组中跟踪。这种优化通过将需要执行的树遍历量减少一个数量级来规避虚拟DOM的大部分开销。

  其次,编译器积极地检测模板中的静态节点、子树甚至数据对象,并在生成的代码中将它们提升到渲染函数之外。这样可以避免在每次渲染时重新创建这些对象,从而大大提高内存使用率并减少垃圾回收的频率。

  第三,在元素级别。编译器还根据需要执行的更新类型,为每个具有动态绑定的元素生成一个优化标志。例如,具有动态类绑定和许多静态属性的元素将收到一个标志,提示只需要进行类检查。运行时将获取这些提示并采用专用的快速路径。

综合起来,这些技术大大改进了我们的渲染更新基准,Vue 3有时占用的CPU时间不到Vue 2的十分之一。

  4.体积变小

  vue向来被称为轻便巧,重写后框架只有10K左右,比原来体积少了一半,并且优化了打包方法,使得打包后的bundle的体积更小。

二、vue3 一些重点API更新情况

  1.插槽优化

  在当前的 Vue 版本中,当父组件重新渲染时,其子组件也必须重新渲染。 使用 Vue 3 ,可以单独重新渲染父组件和子组件。

  2.虚拟dom优化

  随着虚拟 DOM 重写,我们可以期待更多的 编译时(compile-time)提示来减少 运行时(runtime)开销。重写将包括更有效的代码来创建虚拟节点。

  3.静态树提升

  使用静态树提升,这意味着 Vue 3 的编译器将能够检测到什么是静态组件,然后将其提升,从而降低了渲染成本。它将能够跳过未整个树结构打补丁的过程。

  4.静态属性提升

  此外,我们可以期待静态属性提升,其中 Vue 3 将跳过不会改变节点的打补丁过程。

  5.基于Proxy的观察者机制

  6. Hooks API (Composition API)

  组合API是Vue的下一个主要版本中最常用的讨论和特色语法。这是一种全新的逻辑重用和代码组织方法。当前,我们使用所谓的Options API构建组件。现在,添加到Vue组件的逻辑通常会采用:如datamethodscomputed等这种方法,最大缺点是这是不JavaScript代码原生方式。您需要确切了解模板中可以访问哪些属性,以及this关键字的行为。在后台,Vue编译器需要将此属性转换为工作代码。因此,我们无法通过从自动建议或类型检查中受益(而大家想一想Typescript,马上就会明白了)。Composition API旨在,通过将组件属性中当前可用的机制公开为JavaScript函数来解决此问题。Vue核心团队将Composition API描述为“一组基于功能的附加API,可以灵活地组合组件逻辑”。用Composition API编写的代码更具可读性,并且都是原生JS,这使它更易于阅读和学习。

  让我们来看一个使用新的Composition API理解其工作原理的组件的简单示例。

<template>
  <button @click="increment">
    Count is: {{ count }}, double is {{ double }}, click to increment.
  </button>
</template>
 
<script>
import { ref, computed, onMounted } from 'vue'
 
export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)
 
    function increment() {
      count.value++
    }
 
    onMounted(() => console.log('component mounted!'))
 
    return {
      count,
      double,
      increment
    }
  }
}
</script>

  mixins的最大缺点是:我们对它实际上添加到我们的组件中一无所知。这不仅使推理变得困难,而且还可能导致名称与现有属性和功能发生冲突。

  使用Composition API来共享代码:

function useCounter() {
  const count = ref(0)
  function increment () { count.value++ }
 
  return {
    count,
    incrememt
  }
}
 
export default {
  setup () {
    const { count, increment } = useCounter()
    return {
      count,
      increment
    }
  }
}

  我们不受模板和组件范围的限制,并且确切地知道可以从计数器访问哪些属性。另外,我们可以从编辑器中可用的代码完成中受益,因为useCounter它只是一个返回某些属性的函数。幕后没有魔力,因此编辑器可以帮助我们进行类型检查和建议。

  7.全局安装/配置API更改

  8.Fragments

  ......

本文参考 公众号 前端大全

慕课网 Brian  Vue3中令人兴奋的新功能