vue3新特性及對比 2.x 的重要變動

今年7月,尤大大宣佈 Vue 3 進入 RC 階段,這意味着 Vue 3 內核 API 與實現已趨穩定。vue

Vue做爲一種漸進式框架, 借鑑了 React 的組件化和虛擬 DOM ,借鑑了 Angular 的模塊化和雙向數據綁定。就框架的 API 而言,對比之下,它更加輕便簡潔。對於Vue的好處這裏再也不贅述。node

相對vue2.x,Vue3作了不少重要的變動,特別是Composition API的引入。咱們知道,Vue 現有 API 是經過「選項」組織代碼的,隨着功能的增加,複雜組件的代碼會變得愈來愈難維護,不少邏輯都沒法複用。開發的時候複雜的組件千行代碼template/data/methods來回翻,看到你頭暈,而vue3可讓你在接手別人的代碼時更容易理清邏輯關係。下面直接進入主題,咱們來看看Vue3都有哪些新特性,相對於以前版本又有哪些變動。react

1、3.0 對比 2.x 的重要變動

Vue 3.0 相對與以前的版本,有 6 個方面的重要變動:數組

Performance(性能)

性能上,主要是優化了虛擬 DOM,因此也就有了更加優化的編譯,同時實現了更加高效的組件初始化。app

  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 變得更加靈活地與其餘框架組合使用。dom

  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 函數能夠返回數組)async

  • 「Just works」
  1. Teleport

Teleport其實就是React中的Portal。Portal 提供了一種將子節點渲染到存在於父組件之外的 DOM 節點的優秀的方案。一個 portal 的典型用例是當父組件有 overflow: hidden 或 z-index 樣式時,但你須要子組件可以在視覺上「跳出」其容器。例如,對話框、懸浮卡以及提示框。ide

  • 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)

2、Composition API 等核心特性

在vue3中引入了Composition API(組合API),使用純函數分隔複用代碼,讓邏輯變得清晰。

關於VCA,有人說跟react的hooks很像,咱們來看看做者尤大的介紹:
image

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還有不少還須要多多學習 : )

相關文章
相關標籤/搜索