• 4/17 beta
  • 7/18 RC https://github.com/vuejs/rfcs...
  • RFCs文档:https://github.com/vuejs/rfcs

对终端用户有显著感知的优化

  • Performance:性能更比v2强
  • Tree shaking support:能够将无用模块“剪辑”,仅打包须要的。
  • Composition API:组合API
  • Fragment, Teleport, Suspense:新增原生性能 “碎片”,Teleport即Protal传送门,“悬念”
  • Better TypeScript support:更优良的Ts反对
  • Custom Renderer API:裸露了自定义渲染API

Performance

  • 重写了虚构dom的实现,且保障了兼容性
  • 编译模板的优化

    • 为什么: vdom 实质还是须要花工夫渲染 diff
    • 怎么做:编译优化次要的计划

      • 基于模版间接生成命令式dom 【 angular】
      • vue2.x的diff形式为全副节点递归比照,vue 3 利用剖析模版优化编译时的diff内容。
    • 保留vdom 起因

      • 思考兼容性 v2 render函数 jsx
      • 提供了一个很好的模版之外更灵便简单的编写dom的形式 尤其对于库作者
  • 更高效的组件初始化

    • 对组件实例创立进行很大优化,比照纯js占用工夫:

      • update性能进步1.3~2倍
      • mount性能进步1.3~1.5倍
      • SSR速度进步了2~3倍

vdom

https://vue-next-template-exp...

Vue提供相似于HTML的模板语法,将模板编译为可返回虚构DOM树的出现函数。通过DIFF算法,递归遍历两个虚构DOM树并比拟每个节点上的每个属性来确定理论DOM的哪些局部须要更新。

传统 vdom 的性能瓶颈

传统 vdom 的性能跟模版大小正相干,跟动静节点的数量无关。在一些组件整个 模版内只有大量动静节点的状况下,这些遍历都是性能的节约。

编译模版的优化

示例:

<div>  <span>static!</span>  <span>{{ msg }}</span>  <span id="id">{{ msg }}</span>  <span :id="id">{{ msg }}</span></div>
  • 划分不同的block及动态节点与动静绑定节点

  • _createVNode办法后的内容叫做patchFlag,是在编译时生成的一个hint,在vnode更新patch阶段标记该节点是否追踪变动
  • / TEXT/ 标记只需对msg的文字变动进行追踪
  • 动静节点的绑定与嵌套层级无关,所有节点间接绑定在根及节点的block中
  • 属性的绑定也雷同:动态书写的属性只有在创立的时候会创立一次,后续不再进行追踪;动静绑定的属性patchFlag会产生对应的变动

优化运行时的内存占用 hoistStatic

把大量的动态节点晋升,进行复用

动态节点会放在渲染函数之外,,也就是在利用启动的时候创立一次,这些动态的虚构节点在每次创立的时候被不停复用,而不是像以前一样在每次更新的时候创立一堆新的vdom对象,销毁旧对象,节俭内存空间

  • 当动态节点数据十分大时,会将动态节点以innerHtml的模式直接插入

事件侦听器缓存cacheHandler

<div>  <span @click="onclick">static!</span></div>

在编译绑定函数时会进行动态剖析,判断是否能够被cache起来。

如果能够被缓存,则会在第一次渲染时会创立这个内联函数并缓存,内联函数中援用最新的绑定函数,后续的更新间接在cache中取同一个函数,也就不须要再扭转,将绑定函数变成动态

即便绑定的是内联函数也会被cache起来

事件缓存在组件中的劣势在组件中更加显著

  • 如果未增加该优化,每次优化都传递新的内联函数,父组件一旦更新,子组件也会跟着更新
  • 类比react hooks,所有的函数在传到子组件的时候都是从新创立的,如果想要确保子组件不更新须要应用useMemo将其包裹起来,Vue实际上是在编译时将useMemo这步操作做好

    很显著,Vue的格调就是让一些轻微的,然而可能会影响到整体性能的小优化,间接在编译时替用户做好。

Tree-shaking

  • 能够将无用模块“剪辑”,仅打包须要的(比方v-model,<transition>,用不到就不会打包)。

    • 咱们在我的项目中都会应用一些构建工具如Webpack,Rollup,这些工具都是有treeshaking性能的, 前提是所有的货色都须要import来写,然而工具对于库自身无奈进行tree-shaking。所以v3自身在对于代码打包的时候会做一些操作,只引入用到的模块。当然,一些必备的的模块如vdom的更新算法及响应式零碎是肯定会蕴含在包里的。
    • 示例

      <div>  <input v-model="model" ></input>  <input v-model="model" type="text"></input>  <input v-model="model" :type="text"></input>  <input v-model="model" type="checkbox"></input></div>
  • 一个简略“HelloWorld”大小仅为:13.5kb

    • 11.75kb,仅Composition API。

      • V2中有一些无奈被tree-shaking的Option API,后续可能会提供一个开关来强制删除这个Option API。
  • 蕴含运行时残缺性能:22.5kb

    • 领有更多的性能,却比v2更迷你。

Composition API 合成API

  • 可与现有的 Options API一起应用
  • 灵便的逻辑组合与复用
  • v3的响应式模块能够和其余框架搭配应用

混入(mixin) 将不再作为举荐应用, Composition API能够实现更灵便且无副作用的复用代码。

能够将Composition API了解为新增加的API,并不影响原有API的应用

官网RFC文档:https://composition-api.vuejs...

详见Composition API 简介

Fragments 碎片

  • 不再限于模板中的单个根节点

    • Vue2中,模版只能有一个根节点,但在Vue3中能够书写多个根节点,在render函数中会将根节点主动变为一个_Fragment 【示例】
  • render函数也能够返回数组了,相似实现了 React.Fragments 的性能 。
  • 'Just works'

Teleport

Teleport原先是对标`React Portal(减少多个新性能,更强)

但因为Chrome有个提案,会减少一个名为Portal的原生element,为防止命名抵触,改为Teleport

次要做的事 :用一种间接申明的形式来将子组件装置到DOM中的其余地位

一个简略示例:

<body>  <div id="app">    <h1>Move the #content with the portal component</h1>    <teleport to="#endofbody">      <div id="content">        <p>          this will be moved to #endofbody.<br />          Pretend that it's a modal        </p>        <Child />      </div>    </teleport>  </div>  <div id="endofbody"></div>  <script>    new Vue({      el: "#app",      components: {        Child: { template: "<div>Placeholder</div>" }      }    });  </script></body>

咱们会失去如下的后果:

  • <teleport>的所有子代-在此示例中:<div id =“ content”>和<Child>将附加到<div id =“ endofbody”>
  • 作为这些子项之一的<Child>组件将依然是<teleport>父级的子组件,也就是说<teleport>是通明的
<div id="app">  <!-- --></div><div id="endofbody">  <div id="content">    <p>      this will be moved to #endofbody.<br />      Pretend that it's a modal    </p>    <div>Placeholder</div>  </div></div>

RFC文档:https://github.com/vuejs/rfcs...

应用文档:https://portal-vue.linusb.org/

suspense

Suspense翻译为:“悬念” 【试验性功能】

  • 可在嵌套层级中期待嵌套的异步依赖项

    • 在一个嵌套的组件树渲染到屏幕上之前,先在内存里进行渲染。在渲染过程中记录所有存在异步依赖的组件,只有在所有的异步依赖都被resolve之后才会把整个树渲染到dom中去
  • 反对async setup()

    • 如果在组件中应用了async setup()函数,这个组件就会被看作异步组件,suspense就会期待async setup()函数中所有的promise都resolve之后,再把整个树渲染进去。
  • 反对异步组件

更好的TypeScript反对

  • Vue 3是用typeScript编写的库,能够享受到主动的类型定义提醒
  • JavaScript 和TypeScript中的API是雷同的。

    • 事实上,代码也基本相同
  • 反对TSX
  • class组件还会持续反对,然而须要引入vue-class-component@next,该模块目前还处在 alpha 阶段。

Vue 3 + TypeScript 插件:

  • 能够在单文件中对模版里的表达式进行类型查看,主动补全等性能。
  • 实质是把模版编译成TS,进行一个操作,再把TS的操作反馈到模版中去

Custom Render API 自定义渲染器API

  • 正在进行NativeScript Vue集成 ,凋谢更多底层性能
  • 用户能够尝试WebGL自定义渲染器,与一般Vue应用程序一起应用(Vugel)。

意味着当前能够通过 vue, Dom编程的形式来进行 webgl 编程 。感兴趣能够看这里:Getting started vugel

试用Vue3路径

  • Codepen
  • 应用Vite通过以下形式启动我的项目:

    npm init vite-app hello-vue3

    vite是一个新工具,法语“快”的意思。 地址:github.com/vuejs/vite

    一个繁难的http服务器,无需webpack编译打包,依据申请的Vue文件,间接发回渲染,且反对热更新(十分快)

其余局部

  • 大多数官网框架部件也提供了v3反对。最新状态:https://github.com/vuejs/vue-...
  • V3文档已更新: v3.vuejs.org(注:新文档(尤其是《迁徙指南》)仍在开发中,将在整个RC阶段持续欠缺。
  • Devtools Beta已获批准,可在Chrome网上利用店中应用(注:devtools须要vue@^3.0.0-rc.1

  • v2还有2.7版本

    • 将有最初一个小版本(2.7)
    • Vue 3向后移植兼容的改良(不损坏兼容性前提下)
    • 加上在Vue 3中删除的性能的弃用正告
    • LTS 18个月。

最初倡议:Vue 3虽好,如果你的我的项目很稳固,且对新性能无过多的要求或者迁徙老本过高,则不倡议降级。

试验性功能

  • <Suspense>
  • <script setup>
  • <style vars>

这些性能现已公布,目标是收集理论应用状况的反馈,但它们可能仍会收到重大更改/重大调整。它们可能会在3.0中放弃试验状态,并最终成为3.1的一部分。

CompositionAPI

v3最大的扭转是退出了这个灵感来源于React Hook的Composition API,这个API将对vue编程产生了根本性改革,然而v3还是兼容v2的Options API。除此之外,还引入了一些不兼容批改,具体可查看Vue3已合并的RFC。

先看一个示例:

<template>  <button @click="increment">    Count is: {{ state.count }}, double is: {{ state.double }}  </button></template><script>import { reactive, computed } from 'vue'export default {  setup(props, context) {    const state = reactive({      count: 0,      double: computed(() => state.count * 2)    })        function increment() {      state.count++    }        return {      state,      increment    }  }}</script>

能够看到v3的组件大体还是和v2统一,由template、script和style组成,作出的扭转有以下几点:

  • 组件减少了setup选项,组件内所有的逻辑都在这个办法内组织,返回的变量或办法都能够在模板中应用。
  • v2中data、computed等选项依然反对,但应用setup时不倡议再应用v2中的data等选项。
  • 提供了reactive、computed、watch、onMounted等抽离的接口代替v2中data等选项。

Options API 与 Composition API

随着Vue的采用率增长,许多用户也正在应用Vue来构建大型项目,这些我的项目是由多个开发人员组成的团队在很长的工夫内重复进行和保护的。

咱们目击了其中一些我的项目遇到了Vue以后API所带来的编程模型的限度。这些问题能够概括为两类:

  1. 随着性能的增长,简单组件的代码变得越来越难以推理。这种状况尤其产生在开发人员正在浏览本人未编写的代码时。根本原因是Vue的现有API通过选项强制执行代码组织,然而在某些状况下,通过逻辑思考来组织代码更有意义。
  2. 不足用于在多个组件之间提取和重用逻辑的洁净且收费的机制。(无关“ 逻辑提取和重用”的更多详细信息)

该RFC中提议的API在组织组件代码时为用户提供了更大的灵活性。当初能够将代码组织为各自解决特定性能的函数,而不用总是通过选项来组织代码。API还使在组件之间甚至内部组件之间提取和重用逻辑变得更加简略。

在RFC的开发动机中也能够看出Composition API是为了解决v2在应答大型简单我的项目时Option API的各种缺点与有余

代码组织

Options API是把代码分类写在几个Option中:

export default {  components: {      },  data() {      },  computed: {      },  watch: {      },  mounted() {      },}

当组件比较简单只有多数逻辑的时候,在各个Option之间来回穿梭还不算麻烦,但当一个vue组件内存在多个逻辑时会怎么呢?

  • 应用Options API时,雷同的逻辑写在不同的中央,各个逻辑的代码穿插错乱,这对保护他人代码的开发者来说绝不是一件简略的事,理分明这些代码都须要破费不少工夫。
  • 而应用Composition API时,雷同的逻辑能够写在同一个中央,这些逻辑甚至能够应用函数抽离进去,各个逻辑之间界线明显,即使保护他人的代码也不会在“读代码”上破费太多工夫。

逻辑抽取与复用

v2中咱们通常应用mixin来实现逻辑复用,但他的问题也非常明显:

  • 命名抵触。mixin的所有option如果与组件或其它mixin雷同会被笼罩
  • 没有运行时实例。顾名思义,mixin不过是把对应的option混入组件内,运行时不存在所抽取的逻辑实例。
  • 涣散的关系。Options API注定所抽取的逻辑的组织是涣散,逻辑外部之间的关系也是涣散的。
  • 宛转的属性减少。mixin退出的option是宛转的,尤其是在有多个mixin的时候,无奈晓得以后属性是哪个mixin的。

在vue3中提供了一种叫Composition Function的形式,这种形式容许像函数般抽离逻辑:

<template>  <div>   <p> {{count}}</p>    <button @click="onClick" :disabled="state">Start</button>    </div></template><script>  import {ref} from 'vue'  // 倒计时逻辑的Composition Function  const useCountdown = (initialCount) => {    const count = ref(initialCount)    const state = ref(false)    const start = (initCount) => {      state.value = true;      if (initCount > 0) {        count.value = initCount      }       if (!count.value) {        count.value = initialCount      }      const interval = setInterval(() => {        if (count.value === 0) {          clearInterval(interval)          state.value = false        } else {          count.value--        }      }, 1000)    }    return {      count,      start,      state    }  }    export default {   setup() {    // 间接应用倒计时逻辑     const {count, start, state} = useCountdown(10)     const onClick = () => {        start()     }     return  {count, onClick, state}   }  }</script>

v3倡议应用如React hook中一样应用use结尾命名抽取的逻辑函数,如上代码抽取的逻辑简直如函数个别,应用的时候也极其不便。

TS类型反对

开发人员在大型项目上的另一个常见性能要求是更好的TypeScript反对。Vue以后的API在与TypeScript集成时提出了一些挑战,这次要是因为Vue依赖单个this上下文来公开属性,并且this在Vue组件中的应用比一般JavaScript更具魔力(例如this嵌套在methods指向组件实例而非methods对象的点下方的外部函数)。换句话说,Vue的现有API只是在设计时就没有思考类型推断,并且在尝试使其与TypeScript完满配合时会产生很多复杂性。

v2对ts的反对次要是通过vue-class-component,还需引入依赖包,且毛病也非常显著:要使Class API解决类型问题,它必须依赖装璜器-这是一个十分不稳固的第2阶段提案,在实现细节方面存在很多不确定性。

相比与v2,v3对ts的反对则好得多:

  • v3中是在setup中进行编程,setup不依赖this,大部分API大多应用一般的变量和函数,这些变量和函数天然是类型敌对的。
  • 用Composition API编写的代码能够享受残缺的类型推断,简直不须要手动类型提醒。这也意味着用提议的API编写的代码在TypeScript和一般JavaScript中看起来简直雷同。
  • 这些接口已取得更好的IDE反对,即便非TypeScript用户也能够从中受害。

Composition API 介绍

setup

setup是vue新增的一个选项,它是组件内应用Composition API的入口。setup中不应用this上下文。

  • 调用工夫

    setup是在创立vue组件实例并实现props的初始化之后执行,也是在beforeCreate钩子之前执行,这意味着setup中无奈应用其它option(如data)中的变量,而其它option能够应用setup中返回的变量。

  • 模板应用

    如果setup返回一个对象,这个对象的所有属性会合并到template的渲染上下文中,也就是说能够在template中应用setup返回的对象的属性。

    <template>  <div>{{ count }} {{ object.foo }}</div></template><script>  import { ref, reactive } from 'vue'  export default {    setup() {      const count = ref(0)      const object = reactive({ foo: 'bar' })      // expose to template      return {        count,        object      }    }  }</script>
  • 与Render Function/jsx联合应用

    setup也能够返回render function,应用办法与vue2中的render办法相似。

    import { h, ref, reactive } from 'vue'export default {  setup() {    const count = ref(0)    const object = reactive({ foo: 'bar' })    return () => h('div', [count.value, object.foo])  }}
  • setup的参数

    setup有两个参数,第一个参数为props,是组件的所有参数,与vue2中一样,参数是响应式的,扭转会触发更新;第二个参数是setup context,蕴含emit等属性。

    const MyComponent = {  setup(props, context) {    context.attrs    context.slots    context.emit  }}
  • this的应用

    this is not available inside setup(). Since setup() is called before 2.x options are resolved, this inside setup() (if made available) will behave quite differently from this in other 2.x options. Making it available will likely cause confusions when using setup() along other 2.x options. Another reason for avoiding this in setup() is a very common pitfall for beginners

    v3中同时反对Options API 与Composition API,因为setup()会在Option API 解析之前调用,而此时setup()外部的this指向与Option API中的齐全不同,十分可能引起凌乱。

    故而,setup中不应用this上下文。

reactive

reactive函数接管一个对象,并返回一个对这个对象的响应式代理。它与v2中的Vue.obserable()是等价的

const obj = reactive({ count: 0 })

ref

ref函数接管一个用于初始化的值并返回一个响应式的和可批改的ref对象。该ref对象存在一个value属性,value保留着ref对象的值。

const count = ref(0)console.log(count.value) // 0count.value++console.log(count.value) // 1

isRef

isRef用于判断变量是否为ref对象。

const unwrapped = isRef(foo) ? foo.value : foo

toRefs

toRefs用于将一个reactive对象转化为属性全副为ref对象的一般对象。

const state = reactive({  foo: 1,  bar: 2})const stateAsRefs = toRefs(state)/*Type of stateAsRefs:{  foo: Ref<number>,  bar: Ref<number>}*/

toRefs在setup或者Composition Function的返回值时能够保障在应用解构语法之后对象仍旧为相应式

import {reactive, toRefs} from 'vue'function useFeatureX() {  const state = reactive({    foo: 1,    bar: 2  })  return state}function useFeature2() {  const state = reactive({    a: 1,    b: 2  })  return toRefs(state)}export default {  setup() {    // 应用解构之后foo和bar都丢失响应式    const { foo, bar } = useFeatureX()    // 即使应用了解构也不会丢失响应式    const {a, b}= useFeature2()    return {      foo,      bar    }  }}

computed

computed函数与vue2中computed性能统一,它接管一个函数并返回一个value为getter返回值的不可扭转的响应式ref对象。

const count = ref(1)const plusOne = computed(() => count.value + 1)console.log(plusOne.value) // 2plusOne.value++ // error,computed不可扭转// 同样反对set和get属性onst count = ref(1)const plusOne = computed({  get: () => count.value + 1,  set: val => { count.value = val - 1 }})plusOne.value = 1console.log(count.value) // 0

readonly

readonly函数接管一个对象(一般对象或者reactive对象)或者ref,并返回一个只读的原始参数对象代理,在参数对象扭转时,返回的代理对象也会相应扭转。如果传入的是reactive或ref响应对象,那么返回的对象也是响应的。

简而言之就是给传入的对象新建一个只读的正本。

const original = reactive({ count: 0 })const copy = readonly(original)watchEffect(() => {  // 原始对象的变动会登程正本的watch  console.log(copy.count)})// 原始对象扭转,正本的值也会扭转original.count++// 正本不可更改copy.count++ // warning!

watchEffect

在追踪其依赖时立刻运行一个函数,并在依赖发生变化时从新运行

const count = ref(0)watchEffect(() => console.log(count.value))// -> logs 0setTimeout(() => {  count.value++  // -> logs 1}, 100)
  • 勾销察看

    接口会返回一个函数,该函数用以勾销察看。

    const stop = watchEffect(() => {  /* ... */})// laterstop() // 勾销之后对应的watchEffect不会再执行
  • 革除副作用(side effect)

    为什么须要革除副作用?有这样一种场景,在watch中执行异步操作时,在异步操作还没有执行实现,此时第二次watch被触发,这个时候须要革除掉上一次异步操作。

    watch(onInvalidate => {  const token = performAsyncOperation(id.value)  onInvalidate(() => {    // id has changed or watcher is stopped.    // invalidate previously pending async operation    token.cancel()  })})

    watch提供了一个onInvalidate的副作用革除函数,该函数接管一个函数,在该函数中进行副作用革除。在以下状况中调用:

    • watch的callback行将被第二次执行时。
    • watch被进行时,即组件被卸载之后

接口还反对配置一些选项以扭转默认行为,配置选项可通过watch的最初一个参数传入。

  • flush
    当同一个tick中产生许多状态渐变时,Vue会缓冲观察者回调并异步刷新它们,以防止不必要的反复调用。

    默认的行为是:当调用观察者回调时,组件状态和DOM状态曾经同步。

    这种行为能够通过flush来进行配置,flush有三个值,别离是post(默认)、presyncsync示意在状态更新时同步调用,pre则示意在组件更新之前调用。

    watchEffect(  () => {    /* ... */  },  {    flush: 'sync'  })
  • onTrack与onTrigger用于调试:

    • onTrack:在reactive属性或ref被追踪为依赖时调用。
    • onTrigger: 在watcher的回调因依赖扭转而触发时调用。
    watchEffect(  () => {    /* side effect */  },  {    onTrigger(e) {      debugger    }  })

onTrack与onTrigger仅实用于开发模式。

watch

相比于v2的watch,Composition API的watch接口不仅仅是将其逻辑抽取进去,更减少了一些性能。

  • 根本用法
const state = reactive({count: 0})const inputRef = ref('')// state.count与inputRef中任意一个源扭转都会触发watchwatch(() => {  console.log('state', state.count)  console.log('ref', inputRef.value)})
  • 指定依赖源
 const state2 = reactive({count: ''}) const ref2 = ref('') // 通过函数参数指定reative依赖源 // 只有在state2.count扭转时才会触发watch watch(   () => state2.count,   () => {     console.log('state2.count',state2.count)     console.log('ref2.value',ref2.value)   })// 间接指定ref依赖源watch(ref2,() => {  console.log('state2.count',state2.count)  console.log('ref2.value',ref2.value)})
  • watch多个数据源
const state = reactive({a: 'a', b: 'b'})watch(  // state.a和state.b任意一个扭转都会触发watch的回调  () => [state.a, state.b],  // 回调的第二个参数是对应上一个状态的值  ([a, b], [preA, preB]) => {    console.log('callback params:', a, b, preA, preB)    console.log('state.a', state.a)    console.log('state.b', state.b)const ref1 = ref(1)const ref2 = ref(2)watch([ref1, ref2],([val1, val2], [preVal1, preVal2]) => {  console.log('callback params:', val1, val2, preVal1, preVal2)  console.log('ref1.value:',ref1.value)  console.log('ref2.value:',ref2.value)})
  • watchEffect的性能及应用办法 watch同样实用 【勾销察看/革除副作用/flush/调试】

Lifecycle Hooks

Composition API当然也提供了组件生命周期钩子的回调。

import { onMounted, onUpdated, onUnmounted } from 'vue'const MyComponent = {  setup() {    onMounted(() => {      console.log('mounted!')    })    onUpdated(() => {      console.log('updated!')    })    onUnmounted(() => {      console.log('unmounted!')    })  }}

比照vue2的生命周期钩子

  • beforeCreate -> 应用 setup()
  • created -> 应用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

更多内容详见 Composition API 文档

链接汇总:

vue3文档

RFCs文档

v3编译代码演示网址

Teleport RFC

Teleport 文档

Getting started vugel

Codepen

github.com/vuejs/vite

官网框架v3反对最新状态

参考资料:

Composition API 文档

尤雨溪B站直播:聊聊 Vue.js 3.0 Beta

抄笔记:尤雨溪在Vue3.0 Beta直播里聊到了这些…

Vue3 Composition API 应用教程

infoQ:Vue3 新API介绍

英文原文

Vue3 设计过程 尤小右

7/18 v3 RC版本

2019/6/8 VueConf state of vue PPT