往年7月,尤大大发表 Vue 3 进入 RC 阶段,这意味着 Vue 3 内核 API 与实现已趋稳固。

Vue作为一种渐进式框架, 借鉴了 React 的组件化和虚构 DOM ,借鉴了 Angular 的模块化和双向数据绑定。就框架的 API 而言,比照之下,它更加轻便简洁。对于Vue的益处这里不再赘述。

绝对vue2.x,Vue3做了很多重要的变更,特地是Composition API的引入。咱们晓得,Vue 现有 API 是通过「选项」组织代码的,随着性能的增长,简单组件的代码会变得越来越难保护,很多逻辑都无奈复用。开发的时候简单的组件千行代码template/data/methods来回翻,看到你头晕,而vue3能够让你在接手他人的代码时更容易理清逻辑关系。上面间接进入主题,咱们来看看Vue3都有哪些新个性,绝对于之前版本又有哪些变更。

一、3.0 比照 2.x 的重要变更

Vue 3.0 绝对与之前的版本,有 6 个方面的重要变更:

Performance(性能)

性能上,次要是优化了虚构 DOM,所以也就有了更加优化的编译,同时实现了更加高效的组件初始化。

  1. Rewritten virtual dom implementation (重写了虚构 DOM)
  2. Compiler-informed fast paths (优化编译)
  3. More efficient component initialization (更高效的组件初始化)
  4. 1.3-2x better update performance (1.3~2 倍的更新性能)
  5. 2-3x faster SSR (2~3 倍的 SSR 速度)

Tree-shaking support (反对 Tree-shaking)

在大部分状况下,咱们并不需要 vue 中的所有性能,然而在之前的 vue 版本中,咱们没有一个适合的方法用来除去不须要的性能,而 Vue3 中,为了满足体积更小的需要,反对 Tree-shaking,也就意味着咱们能够按需要援用的内置的指令和办法。

  1. Most optional features (e.g. v-model, <transition>) are now tree-shakable (大多数可选性能(如 v-model、<transition>)当初都是反对 Tree-shaking 的。)
  2. Bare-bone HelloWorld size: 13.5kb. 11.75kb with only Composition API support.
  3. All runtime features included: 22.5kb. More features but still lighter than Vue 2.

Composition API

Composition API 次要是进步了代码逻辑的可复用性,并且将 Reactivity 模块独立进去,这也使得 vue 3 变得更加灵便地与其余框架组合应用。

  1. Usable alongside existing Options API (可与现有选项 API 一起应用)
  2. Flexible logic composition and reuse (灵便的逻辑组成和重用)
  3. Reactivity module can be used as a standalone library (Reactivity 模块能够作为独立的库应用)

Fragment, Teleport, Suspense

  1. Fragment

在书写vue2时,因为组件必须只有一个根节点,很多时候会增加一些没有意义的节点用于包裹。Fragment组件就是用于解决这个问题的(这和React中的Fragment组件是一样的)。

  • No longer limited to a single root node in templates (<template> 中不再局限于繁多的根节点)

-Manual render functions can simply return Arrays (render 函数能够返回数组)

  • “Just works”
  1. Teleport

Teleport其实就是React中的Portal。Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划。一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 款式时,但你须要子组件可能在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。

  • Previously known as <Portal>(原名为 <Portal>)
  • More details to be shared by @Linusborg
  1. Suspense

同样的,这和React中的Supense是一样的。Suspense 让你的组件在渲染之前进行“期待”,并在期待时显示 fallback 的内容。

  • Wait on nested async dependencies in a nested tree
  • Works with async setup() (与 async 函数 setup() 配合应用)
  • Works with Async Components (与 Async 组件配合应用)

Better TypeScript support (更好的 TypeScript 反对度)

vue3.0 对 TS 的反对度更高了,同时也反对 TSX 的应用;API 在 JS 与 TS 中的应用雷同;类组件依然可用,然而须要咱们引入 vue-class-component@next,该模块目前还处于 alpha 测试阶段。

  1. Codebase written in TS w/ auto-generated type definitions
  2. API is the same in JS and TS
  3. In fact, code will also be largely the same
  4. TSX support
  5. Class component is still supported (vue-class-component@next is currently in alpha)

Custom Renderer API (自定义的 Renderer API)

自定义 render 会提供一个 API 用来创立自定义的 render,因而不再须要为了自定义一些性能而 fork Vue 的代码。这个个性给 Weex 和 NativeScript Vue 这样的我的项目提供了很多便当。

  1. NativeScript Vue integration underway by @rigor789
  2. Users already experimenting w/ WebGL custom renderer that can be used alongside a normal Vue application (Vugel)

二、Composition API 等外围个性

在vue3中引入了Composition API(组合API),应用纯函数分隔复用代码,让逻辑变得清晰。

对于VCA,有人说跟react的hooks很像,咱们来看看作者尤大的介绍:

Lifecycle Hooks

新版的生命周期函数,能够按需导入到组件中,且只能在 setup() 函数中应用.

  1. beforeCreate -> use setup()
  2. created -> use setup()
  3. beforeMount -> onBeforeMount
  4. mounted -> onMounted
  5. beforeUpdate -> onBeforeUpdate
  6. updated -> onUpdated
  7. beforeDestroy -> onBeforeUnmount
  8. destroyed -> onUnmounted
  9. errorCaptured -> onErrorCaptured
 import {onBeforeMount, onMounted} from 'vue' export default {     setup() {         onBeforeMount(() => {             console.log('onBeforeMount')         })        onMounted(() => {            console.log('onMounted')        })     }, }

setup

setup() 函数是 vue3 中,专门为组件提供的新属性。它为咱们应用 vue3 的 Composition API 新个性提供了对立的入口。

  1. setup 函数相当于 vue2.x 中 beforeCreate 和 created, 在 beforeCreate 之后、created 之前执行
  2. setup第一个形参,接管 props 数据
  3. setup第二个形参是一个上下文对象, 在 setup() 函数中无法访问到 this, 能够用这个context来拜访
 export default {     props: {        str: String     },     setup(props, context) {         console.log(props) // str         console.log(context)  // attrs, slots, parent, root, emit, refs     }, }

reactive

reactive() 函数接管一个一般对象,返回一个响应式的数据对象。

 import { reactive } from 'vue' export default {     setup() {         const state = reactive({count: 0}) // 创立响应式数据对象         return state //将响应式数据对象 return 进来,供 template 应用     } }

ref

ref() 函数依据给定的值创立一个响应式的数据对象,返回值是一个对象,这个对象上只蕴含一个 .value 属性

 import { ref } from 'vue' export default {     setup() {         const count = ref(0) // 创立响应式数据对象 count,初始值为 0         console.log(count.value) // 在setup内拜访count值须要.value 属性才能够,但在template中能够间接拜访         return {            count         }     }, }

toRefs

toRefs() 函数能够将 reactive() 创立进去的响应式对象,转换为一般的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据

 import { reactive, toRefs } from 'vue' export default {     setup() {         const state = reactive({count: 0, name:'weedsFly'}) // 用reactive集中创立多个响应式对象         const add = () => { // methods写在setup内             state.count++         }         return {             // ...state,  // 应用开展运算符后 用reactive创立的响应式数据 变成了 固定的值             ...toRefs(state), // 能够用toRefs函数 将传进来的非响应式对象 转成 ref() 类型的响应式数据             add        }    }, }

ref援用

通过 ref() 还能够援用页面上的元素或组件,这点和vue2的ref概念相似。

1.元素援用

 import { ref, onMounted } from 'vue' export default {     setup() {         const h1Ref = ref(null) // 创立一个 DOM 援用         onMounted(() => { // 在 DOM 首次加载结束之后,能力获取到元素的援用             h1Ref.value.style.color = 'pink' // h1Ref.value 是原生DOM对象         })         return {            h1Ref         }     } }

2.组件援用

 //父组件: import { ref } from 'vue' export default {     setup() {         const compRef = ref(null) // 创立一个组件的 ref 援用         showCompData = () => { // 展现子组件中 count 的值            console.log(compRef.value.count)          }         return {             compRef,             showCompData         }     } }
 //子组件: import { ref } from 'vue' export default {     setup() {         const count = ref(0) // 定义响应式的数据         return {            count         }     } }

computed

omputed() 用来创立计算属性,computed() 函数的返回值是一个 ref 的实例。

1.computed创立只读的计算属性(传入一个 function 函数,能够失去一个只读的计算属性)

 import { ref, computed } from 'vue' export default {     setup() {         const count = ref(0)         const computedCount = computed(() => count.value + 1)         computedCount.value = 9 //  computed value is readonly.         return {             count,             computedCount         }     }, }

2.computed创立可读可写的计算属性

 import { ref, computed } from 'vue' export default {     setup() {         const count = ref(0)         const computedCount = computed({             get: () => count.value + 1,             set: val => {                count.value = val - 1             }         })         computedCount.value = 100 // 为计算属性赋值的操作,会触发 set 函数          console.log(count.value) // 触发 set 函数后,count 的值会被更新         return {             count,             computedCount,             computedCount         }     }, }

watch

1.监督单个数据源变动

  • 监督单个reactive创立的数据源
 import { reactive, watch } from 'vue' export default {     setup() {         const state = reactive({count: 100})          watch(             () => state.count,             (newVal, oldVal) => { console.log(newVal, oldVal)},             {lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码         )         setTimeout(() => {            state.count++         }, 1500)     } }
  • 监督单个ref创立的数据源
 import { ref, watch } from 'vue' export default {     setup() {         const count = ref(100)          watch(             count,             (newVal, oldVal) => { console.log(newVal, oldVal)},             {lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码         )         setTimeout(() => {            count++         }, 1500)     } }

2.监督多个数据源

  • 监督多个reactive创立的数据源
 import { reactive, watch } from 'vue' export default {     setup() {     const state = reactive({count: 100, name: 'Laiyj'})     watch(         [() => state.count, () => state.name],         ([newCount, newName], [oldCount, oldName]) => {              console.log(newCount, oldCount)             console.log(newName, oldName)         },         {lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码     )     setTimeout(() => {         state.count++         state.name = 'Lucy'     }, 1000)     } }
  • 监督多个ref创立的数据源
 import { ref, watch } from 'vue' export default {     setup() {         const count = ref(100)         const name = ref('Laiyj')         watch(             [count, name],             ([newCount, newName], [oldCount, oldName]) => {                 console.log(newCount, oldCount)                 console.log(newName, oldName)             },             {lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码         )         setTimeout(() => {             count++             name = 'Lucy'         }, 1000)     } }

3.革除watch监督

 import { ref, watch } from 'vue' export default {     setup() {         const count = ref(100)         const stop = watch( // 创立监督,并失去 进行函数             count,             (newVal, oldVal) => {                 console.log('I am watching.')                 console.log(newVal, oldVal)             },             {lazy: true} // 在 watch 被创立的时候,不执行回调函数中的代码         )         setTimeout(() => {            count++         }, 1000)         const clearWatch = () => {            stop()         }         return {             count,             clearWatch         }     } }

4.在 watch 中革除有效的异步工作(与节流防抖同效)

 import { ref, watch } from 'vue' export default {     setup() {         const keyword = ref('')         const asyncPrint = (val) => { // 执行异步工作,并失去敞开异步工作的 timerId             return setTimeout(() => {                console.log(val)             }, 1000)         }         watch(             keyword,             (newVal, oldVal, onClean) => {                 const timeId = asyncPrint()                 onClean(() => {clearTimeout(timeId)}) // 如果 watch 监听被反复执行了,则会先革除上次未实现的异步工作             }         )         return {             keyword         }     } }

provide & inject

  1. provide() 和 inject() 能够实现嵌套组件之间的数据传递。
  2. 这两个函数只能在 setup() 函数中应用。
  3. 父级组件中应用 provide() 函数向下传递数据;子级组件中应用 inject() 获取下层传递过去的数据。
 //父组件: import { provide, watch } from 'vue' export default {     setup() {         const color = ref('red')         provide('themeColor', color) // 父组件通过 provide 函数向子级组件共享数据(不限层级) provide('要共享的数据名称', 被共享的数据)         const changeColor = (val) => {            color.value = val         }         return {             color,             changeColor         }     } }
 // 子级组件: import { inject, watch } from 'vue' export default {     setup() {         const color = inject('color') // 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据         return {            color         }     } }

以上只列举了几个比拟根底重要的API,vue3还有很多还须要多多学习 : )