1. 程式人生 > 其它 >Vue3.0相對於Vue2.X的改變

Vue3.0相對於Vue2.X的改變

目錄

常用Composition API

拉開序幕的setup

  • 理解:Vue3.0中一個新的配置項,值為一個函式。

  • setup是所有Composition API(組合API)“表演的舞臺”。

  • 元件中所用到的:資料、方法等等,均要配置在setup中。

  • setup函式的兩種返回值:

    • 若返回一個物件,則物件中的屬性、方法,在模板中均可以直接使用。(重點關注! )
    • 若返回一個渲染函式:則可以自定義渲染內容。(瞭解)
  • 注意點:

    • 儘量不要與Vue2.x配置混用

      • Vue2.x配置(data、methos、computed...)中可以訪問到setup中的屬性、方法。
      • 但在setup中不能訪問到Vue2.x配置(data、methos、computed...)。
        如果有重名, setup優先。
    • setup不能是一個async函式,因為返回值不再是return的物件,而是promise,模板看不到return物件中的屬性。(後期也可以返回一個Promise例項,但需要Suspense和非同步元件的配合)

ref函式

  • 作用:定義一個響應式的資料

  • 語法:const xxx = ref(initValue)

    • 建立一個包含響應式資料的引用物件(reference物件)。
    • JS中操作資料:xxx.value
    • 模板中讀取資料:不需要.value,直接:<div>{{xxx}}</div>
  • 備註:

    • 接收的資料可以是:基本型別、也可以是物件型別。
    • 基本型別的資料:響應式依然是靠object.defineProperty()getset完成的。
    • 物件型別的資料:內部“求助”了Vue3.0中的一個新函式——reactive函式。

reactive函式

  • 作用:定義一個物件型別的響應式資料(基本類型別用它,用ref函式)
  • 語法:const代理物件= reactive(被代理物件)接收一個物件(或陣列),返回一個代理器物件(proxy物件)
  • reactive定義的響應式資料是"深層次的”。
  • 內部基於ES6的 Proxy 實現,通過代理物件操作源物件內部資料都是響應式的

Vue3.0中的響應式原理

vue2.x的響應式

  • 實現原理:
    • 物件型別:通過 object.defineProperty()對屬性的讀取、修改進行攔截(資料劫持)。
    • 陣列型別:通過重寫更新陣列的一系列方法來實現攔截。(對陣列的變更方法進行了包裹)。
Object.defineProperty(data,'count',{
get () {},
set () {}
})
  • 存在問題:
    • 新增屬性、刪除屬性,介面不會更新。
    • 直接通過下標修改陣列,介面不會自動更新。

vue3.0的響應式

  • 實現原理:
    • 通過Proxy(代理):攔截物件中任意屬性的變化,包括:屬性值的讀寫、屬性的新增、屬性的刪除等
    • 通過Reflect(反射):對被代理物件的屬性進行操作。
    • MDN文件中描述的ProxyReflect
new Proxy(data,{
    //攔截讀取屬性值
    get (target,prop) {
        return Reflect.get(target,prop)
    },
    //攔截設定屬性值或新增新屬性
    set (target, prop, value) {
        return Reflect.set(target,prop,value)
    },
    //攔截刪除屬性
    deleteProperty (target,prop) {
        return Reflect.deleteProperty(target,prop)
    }
})

proxy.name = 'tom'

reactive對比ref

  • 從定義資料角度對比:

    • ref用來定義:基本型別資料。
    • reactive用來定義:物件(或陣列)型別資料。
    • 備註:ref也可以用來定義物件(或陣列)型別資料,它內部會自動通過reactive轉為代理物件。
  • 從原理角度對比:

    • ref通過Object.defineProperty()get與set來實現響應式(資料劫持)。
    • reactive通過使用Proxy來實現響應式(資料劫持)﹐並通過Reflect操作源物件內部的資料。
  • 從使用角度對比:

    • ref定義的資料:操作資料需要.value,讀取資料時模板中直接讀取不需要.value
    • reactive定義的資料:操作資料與讀取資料:均不需要.value

setup的兩個注意點

  • setup執行的時機

    • 在beforeCreate之前執行一次,this是undefined
  • setup的引數

    • props:值為物件,包含:元件外部傳遞過來,且元件內部宣告接收了的屬性。
    • context:上下文物件
      • attrs:值為物件,包含:元件外部傳遞過來,但沒有在props配置中宣告的屬性,相當於this.$attrs
      • slots:收到的插槽內容,相當於this.$slots
      • emit:分發自定義事件的函式,相當於this.$emit

計算屬性與監視

computed函式

  • 與Vue2.x中computed配置功能一致
  • 寫法
import {computed} from 'vue'

setup(){
    ...
    //計算屬性--簡寫
    let fullName = computed(()=>{
        return person.firstName + '-' +person.lastName
    })
    //計算屬性--完整
    let fullName = computed({
        get(){
            return person.firstName + '-' +person. lastName
        },
        set(value){
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
        }
    })
}

watch函式

  • Vue2.x中watch配置功能一致
  • 兩個注意點:
    • 監視reactive定義的響應式資料時:oldValue無法正確獲取、強制開啟了深度監視(deep配置失效)
    • 監視reactive定義的響應式資料中某個屬性時:deep配置有效。
setup(){
//資料
    let sum = ref(0)
    let msg = ref('你好')
    let person = ref({
        name:'張三',
        age:18,
        job:{
            j1:{
                salart:20
            }
        }
    })
}



//情況一:監視ref定義的響應式資料
watch(sum,(newValue,oldValue)=>{
    console.log('sum變化了' ,newValue,oldValue)
}, {immediate:true})

//情況二:監視多個ref定義的響應式資料
watch([sum,msg],(newValue,oldValue)=>{
    console.log('sum或msg變化了', newValue,oldValue)
})

/* 情況三:監視reactive定義的響應式資料
若watch監視的是reactive定義的響應式資料,則無法正確獲得oldvalue! !
若watch監視的是reactive定義的響應式資料,則強制開啟了深度監視
*/

watch(person, (newValue,oldValue)=>{
    console.log('person變化了',newValue,oldValue)
}, {immediate:true,deep:false})//此處的deep配置不再奏效

//情況四:監視reactive定義的響應式資料中的某個屬性
watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job變化了',newValue,oldvalue)
},{immediate:true, deep:true})

//情況五:監視reactive所定義的一個響應式資料中的某些屬性
watch([()=>person.name,()=>person.age], (newValue,oldValue)=>{
console.log('person的name或age變化了',newValue, oldValue)
})

//特殊情況
watch(()=>person.job, (newValue,oldValue)=>{
console.log('person的job變化了',newValue,oldValue)
},{deep:true})//此處由於監視的是reactive素定義的物件中的某個屬性,所以deep配置有效

watchEffect函式

  • watch的套路是:既要指明監視的屬性,也要指明監視的回撥。
  • watchEffect的套路是:不用指明監視哪個屬性,監視的回撥中用到哪個屬性,那就監視哪個屬性。
  • watchEffect有點像computed:
    • computed注重的計算出來的值(回撥函式的返回值),所以必須要寫返回值。
    • watchEffect更注重的是過程(回撥函式的函式體),所以不用寫返回值。
//watchEffect所指定的回撥中用到的資料只要發生變化,則直接重新執行回撥。watchEffect(()=>{
    const x1 = sum.value
    const x2 = person.age
    console.log('watchEffect配置的回撥執行了')
})

生命週期

  • Vue3對比Vue2中有改動的生命週期鉤子

beforeDestroy改名為beforeUnmount
destroyed改名為unmounted

  • Vue3.0也提供了Composition API形式的生命週期鉤子,與Vue2.x中鉤子對應關係如下:
    • beforeCreate===>setup()
    • created========>setup()
    • beforeMount====>onBeforeMount
    • mounted========>onMounted
    • beforeUpdate===>onBeforeUpdate
    • updated========>onUpdated
    • beforeUnmount==>onBeforeUnmount
    • unmounted======>onUnmounted

Vue3生命週期圖示:

自定義hook函式

  • 什麼是hook?——(本質是一個函式)把setup函式中使用的Composition APl進行了封裝。
  • 類似於vue2.x中的mixin。
  • 自定義hook的優勢:複用程式碼,讓setup中的邏輯更清楚易懂。

toRef

  • 作用:建立一個ref物件,其value值指向另一個物件中的某個屬性值。
  • 語法:const name = toRef(person , 'name')
  • 應用:要將響應式物件中的某個屬性單獨提供給外部使用時。
  • 擴充套件:toRefstoRef功能一致,但可以批量建立多個ref物件,語法:toRefs(person)

其它Composition API

shallowReactive與shallowRef

  • shallowReactive:只處理物件最外層屬性的響應式(淺響應式)。
  • shallowRef:只處理基本資料型別的響應式,不進行物件的響應式處理。
  • 什麼時候使用?
    • 如果有一個物件資料,結構比較深,但變化時只是外層屬性變化===> shallowReactive
    • 如果有一個物件資料,後續功能不會修改該物件中的屬性,而是生新的物件來替換===> shallowRef

toRaw與markRaw

  • toRaw:

    • 作用:將一個由reactive生成的響應式物件轉為普通物件
    • 使用場景:用於讀取響應式物件對應的普通物件,對這個普通物件的所有操作,不會引起頁面更新。
  • markRaw:

    • 作用:標記一個物件,使其永遠不會再成為響應式物件。
    • 應用場景:
      • 有些值不應被設定為響應式的,例如複雜的第三方類庫等。
      • 當渲染具有不可變資料來源的大列表時,跳過響應式轉換可以提高效能。

customRef

  • 作用:建立一個自定義的ref,並對其依賴項跟蹤和更新觸發進行顯式控制。
  • 實現防抖效果:
<template>
<input type="text" v-model="keyword"><h3>{{keyword}}</h3>
</template>

<script>
import {ref,customRef} from 'vue'
    export default {
        name:'Demo',
        setup(){
        // let keyword = ref('hello')
        //使用vue準備好的內建ref
            //自定義一個myRef
            function myRef(value,delay){
                let timer
                //通過customRef去實現自定義
                return customRef((track,trigger)=>{
                    return{
                        get(){
                            track()//告訴vue這個value值是需要被“追蹤”的
                            return value
                        },
                        set(newValue){
                            clearTimeout(timer)
                            timer = setTimeout(()=>{
                                value = newValue
                                trigger()//告訴vue去更新介面
                            }, delay)
                        }
                    }
                })
            }
            let keyWord = myRef('hello',500)//使用程式設計師自定義的ref
            return {keyWord}
        }
    }
</script>

provide與inject

  • 作用:實現祖孫元件間通訊

  • 套路:父元件有一個provide選項來提供資料,後代元件有一個inject選項來開始使用這些資料

  • 具體寫法:

    • 祖元件中:

      setup(){
          ......
          let car = reactive({name:'賓士',price:' 40萬'})
          provide('car' ,car)
          ......
      }
      
    • 後代元件中:

      setup(props,context){
          ......
          const car = inject('car')
          return {car}
          ......
      }
      

響應式資料的判斷

  • isRef:檢查一個值是否為一個ref物件
  • isReactive:檢查一個物件是否是由reactive建立的響應式代理
  • isReadonly:檢查一個物件是否是由readonly建立的只讀代理
  • isProxy:檢查一個物件是否是由reactive或者readonly方法建立的代理

Composition API的優勢

Options API存在的問題

使用傳統OptionsAPI中,新增或者修改一個需求,就需要分別在data,methods,computed裡修改。

omposition API的優勢

我們可以更加優雅的組織我們的程式碼,函式。讓相關功能的程式碼更加有序的組織在一起。

新的元件

Fragment

  • 在Vue2中:元件必須有一個根標籤
  • 在Vue3中:元件可以沒有根標籤,內部會將多個標籤包含在一個Fragment虛擬元素中。好處:減少標籤層級,減小記憶體佔用

Teleport

  • 什麼是Teleport?——Teleport是一種能夠將我們的元件html結構移動到指定位置的技術。
<teleport to="移動位置">
    <div v-if="isShow" class="mask">
        <div class="dialog">
            <h3>我是一個彈窗</h3>
            <button @click="isShow = false">關閉彈窗</ button>
        </ div>
    </div>
</teleport>

Suspense

  • 等待非同步元件時渲染一些額外內容,讓應用有更好的使用者體驗
  • 使用步驟:
    • 非同步引入元件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
  • 使用Suspense包裹元件,並配置好defaultfallback
<template>
    <div class="app">
        <h3>我是App元件</h3>
        <Suspense>
            <template v-slot:default>
                <Child/>
            </template>
            <template v-slot:fallback>
                <h3>載入中.....</h3>
            </template>
        </Suspense>
    </ div>
</template>

其它

全域性API的轉移

  • Vue 2.x有許多全域性API和配置。
    • 例如:註冊全域性元件、註冊全域性指令等。
//註冊全域性元件
Vue.component('MyButton',{
    data:() =>({
        count:0
    }),
    template:'<button @click="count++">Clicked {{ count }} times.</button>'
})
//註冊全域性指令
Vue.directive('focus',{
    inserted:el => el.focus()
}
  • Vue3.0中對這些API做出了調整:
    • 將全域性的API,即:Vue.xxx調整到應用例項( app )上
2.x全域性API( vue ) 3.x例項API ( app
Vue.config.XXXX app.config.XXXX
Vue.config.productionTip(生產提示) 移除
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

其它改變

  • data選項應始終被宣告為一個函式。

  • 過度類名的更改:

    • Vue2.x寫法

      .v-enter,
      .v-leave-to {
          opacity:0;
      }
      .v-leave,
      -v-enter-to {
          opacity: 1;
      }
      
    • Vue3.x寫法

      -v-enter-from,
      .v-leave-to {
          opacity : 0;
      }
      
      .v-leave-from,
      .v-enter-to {
      opacity: 1;
      }
      
  • 移除keyCode作為v-on 的修飾符,同時也不再支援config.keyCodes

  • 移除v-on.native修飾符

    • 父元件中繫結事件

      <my-component
          v-on:glose="handleComponentEvent"
          v-on:click="handleNativeclickEvent"
      />
      
    • 子元件中宣告自定義事件

      <script>
          export default {
              emits: ['close']
          }
      </script>
      

移除過濾器(filter)

過濾器雖然這看起來很方便,但它需要一個自定義語法,打破大括號內表示式是“只是JavaScript”的假設
這不僅有學習成本,而且有實現成本!建議用方法呼叫或計算屬性去替換過濾器。