computed 計算屬性
在vue3中,雖然也能寫vue2的computed,但還是更推薦使用vue3語法的computed。
在Vue3中,計算屬性是組合式API,要想使用computed,需要先對computed進行引入:
import { computed } from 'vue'
computed是一個函數。它的參數接收一個函數,這個函數可以是普通函數,也可以是箭頭函數,由于vue3中沒有this,所以無需關注函數類型對this指向的影響。這個函數就是計算的方法函數。
<template>{{ totName }}
</template><script>import { computed } from 'vue'export default{setup(){let name = 'dog'let age = 12let totName = computed( ()=>{return name + age;})return{totName,}}}
</script>
但是通過這種方式定義的計算屬性,是只讀的,無法對屬性進行修改。如果計算屬性同時需要讀取和修改,要通過下面這種形式:
傳入computed的參數是一個對象,在對象中配置get和set。
<template>{{ totName }}<button @click=change>click</button>
</template><script>import { ref,computed } from 'vue'export default{setup(){let name = ref('dog')let age = ref(12)let totName = computed( {get(){return name.value + age.value;},set(value){name.value = value[0];age.value = value[1];}})function change(){totName.value = ['cat',10];}return{totName,change}}}
</script>
watch
在vue3中,watch也是組合式API,也需要引入:
import { watch } from 'vue'
與其他組合式API一樣,其實組合式API就是Vue3提供的一些函數,因此watch也是一個函數。
watch的第一個參數是需要監視的數據,第二個參數類似handler函數,當要監視的數據改變時,調用這個函數,函數提供兩個參數 監視數據新的值,監視數據舊的值。第三個參數是配置對象,可以用于配置immediate和deep。
在vue2中,watch配置項只有一個,但是在vue3中,可以使用多次watch函數。
如果想用vue3監控多個數據的改變,可以寫多個watch函數,也可以把第一個參數寫成數組,數組中的每個元素都是想要監視的數據。第二個參數中,newValue和oldValue也是數組的形式,數組中第n個元素對應第n個被監視的數據的newValue和oldValue。
要注意:
如果watch監視的數據是一個對象,當修改了對象中某個屬性的數據,在watch的回調函數中,newValue和oldValue值是一樣的。也就是說,通過ref定義的普通類型響應式數據,用watch進行監測,new和old的值才是正確的。如果通過ref或reactive創建的響應式對象,在watch中無法獲取正確的new和old值。如果是ref定義的對象,需要在監測對象里寫.value語法,且和reactive一樣,newValue和oldValue都是newValue。這個問題目前無法解決,但是,由于在開發中,需要oldValue的情況并不多,如果確實需要oldValue,要把監測的數據定義成普通類型的數據。
除此之外,如果監測的數據是用reactive創建的響應式,即使對象的層級較多,不設置deep:true,也能對對象進行深度監測。如果不需要深度監測,需要設置deep:false。
如果只想監測對象中某個具體的屬性,而不是整個對象中所有屬性,在第一個參數上寫對象.屬性,用這種語法,監測是無法生效的。watch只能監測ref、reactive。如果想要監測對象上某個具體的屬性,第一個參數應該是一個函數,函數的返回值是對象.屬性,watch才能夠正常監測。而且,這時候newValue和oldValue是正確的數值。
如果想監視對象中的某些屬性,需要寫一個數組,數組中每個元素都是函數,函數的返回值是需要監視的數據,且數據是普通數據類型。通過這種語法,可以正確獲取newValue和oldValue。
但如果要監測的是對象中的某個屬性,這個屬性還是一個對象,此時需要開啟deep:true,否則無法監測到數據的變更。
也就是說,對于watch,根據監測對象的不同,會產生以下現象:
1.如果監測整個對象,默認強制開啟deep:true。
2.如果監測函數中的某個屬性,需要以函數返回值的形式返回這個屬性。
3.如果想要正確監測對象中的對象屬性,需要打開deep:true設置。
watchEffect
watchEffect是一個函數,使用之前需要先引入:
import { watchEffect } from 'vue'
watchEffect的參數是一個回調函數,它會監測內部的回調函數涉及了哪些變量,當涉及的變量數值發生變化時,回調函數就會被觸發。watchEffect初始就會被執行一次,然后當內部涉及的數據變化時也會執行。
watchEffect和watch和computed:
watchEffect和watch都是用于監測數據,在數據發生變化時調用回調函數,但是不同的是,watch既要知名監視的屬性,也要指明回調。watchEffect不需要指明監視的屬性,只需要指明回調函數,在回調函數中用到哪個屬性,就會監視涉及的屬性。
watchEffect和computed:watchEffect回調函數的觸發邏輯和computed類似,都是初始時會執行一次,然后當內部依賴的數據發生變化時,執行一次。但computed是計算屬性,需要返回計算值。watchEffect是監視屬性,它關注過程,無需返回值。
Vue3生命周期
在vue3中,銷毀組件時,觸發的是beforeUnmount和unmounted鉤子,而沒有beforeDestroy和destroyed鉤子。在vue3中,稱為卸載。
對于vue2來說,如果調用new Vue時,沒有傳遞el,那么會走到created鉤子之后,才會判斷el是否配置了。但在vue3中,在一開始,創建了app實例之后,需要進行app.mount,然后才進入生命周期。
在vue3中,當組件卸載時,會觸發beforeUnmount鉤子和unmount鉤子。vue3把生命周期分為掛載->卸載流程。除此之外,vue3生命周期的各種概念和邏輯都跟vue2一樣。
但是,vue3中,如果想通過setup組合式API去使用生命周期鉤子,鉤子的名稱會發生變化:
beforeCreate -> setup()
created -> setup()
在vue3中,并沒有給beforeCreate和created鉤子提供API,但可以把setup看做beforeCreate和created。
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeUnmount -> onBeforeUnmount
unmounted -> onUnmounted
<script>import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'export default{setup(){onBeforeMount(()=>{console.log('onbeforeMount')})onMounted(...)onBeforeUpdate(...)onUpdated(...)onBeforeUnmount(...)onUnmounted(...)}}
</script>
要使用setup組合式API,都需要先進行引入,他們都是函數。
這些函數都接收一個函數作為參數,在對應的時機,這個參數函數就會被調用。
在vue3中,由于可以同時定義生命周期配置項,以及setup中的組合式API生命鉤子,有時候會產生令人難以理解的執行順序,如果同時定義了配置項和組合式,實際上,在掛載時,setup()函數是最先被執行的->beforeCreate->created->onBeforeMount->beforeMount->onMounted->mounted。更新時的執行順序是onBeforeUpdate->beforeUpdate->onUpdated->updated。卸載時的執行順序是onBeforeUpdate->beforeUpdate->onupdated->updated。也就是說,組合式API生命周期的執行時機比配置項快一些。
但是一般不推薦同時使用生命周期配置項和setup組合式API生命鉤子。
hooks
hook也是一個函數,它把setup函數中使用的組合式API進行了封裝。
原始功能:
<template><h2>{{ num }}</h2><button @click=change>click</button>
</template><script>import { onMounted, ref } from 'vue'export default{setup(){let num = ref(0);function change(){num.value++;}onMounted(()=>{num.value = 5;})return{num,change,}}}
</script>
對于某個功能,可以把這個功能涉及的數據、方法、生命周期邏輯抽出來,抽成一個函數,把這個函數單獨放在js中,函數返回功能需要的數據或方法,并對外暴露。當vue文件引入這個js時,就可以復用這個功能(因為vue文件獲取了這個函數,函數里包含了使用功能需要的所有代碼邏輯)。
一般這個js文件就是一個hook的思想,它一般放在src下的hooks文件里。
hook類似于vue2的mixin。通過自定義hook,可以復用代碼,讓setup中的邏輯更清晰。
組合式API
除了前面學到的組合式API,vue3中還具有很多其他的API,這里先學習其中一部分剩余API。
toRef
toRef用于創建一個ref對象,它的value值指向另一個對象中的某個屬性。
有時候,希望拿一個變量存儲對象中的屬性,而且希望對象中的屬性改變時,變量中存儲的屬性也發生改變,當變量中數據改變時,有時候也希望反饋到對象上。
直接通過變量存儲對象屬性時,無法達成這種效果。
但toRef可以把一個并非ref響應式的數據變成ref。toRef接收兩個參數,第一個參數是目標對象,第二個參數是想創建ref的對象上的屬性名。如果想ref的屬性是嵌套的,可以通過第一個參數設置。
toRefs
可以用于批量創建引用。
toRef一次只能處理一個屬性,toRefs一次可以處理多個屬性。
toRefs(對象)
toRefs可以用于對整個對象創建響應式副本。
<template><h2>{{ cat }}</h2><h2>{{ name }}</h2><h2>{{ hobby }}</h2>
</template><script>import { reactive,toRef,toRefs } from 'vue';export default{setup(){let data = reactive({name:'cat',age:10,hobby:{sleep:'day',walk:'night',}})let name = toRef(data,'name');let hobby = toRef(data.hobby,'sleep');let cat = toRefs(data); return {name,hobby,cat}}}
</script>
shallowReactive
淺Reactive。
只實現對象中的第一層數據的響應式,如果第二層數據發生變化,不觸發響應式。
shallowRef
淺Ref。
不處理對象響應式,如果傳入的是對象,無法實現響應式。
shallowReactive與shallowRef:
shallowReactive只處理對象最外層屬性的響應式。shallowRef只處理基本數據類型的響應式,不對對象進行響應式。
使用場景:
如果對象的層級結構很深,但數據變化時只涉及最外層的變化,就可以使用shallowReactive創建響應式。
如果一個對象,后續不會修改對象中的數據,而是生成新對象來替換,可以使用shallowRef(shallowRef可以監測到對象整體的替換修改,但是無法監測到內部屬性的變化)。
readonly
讓一個響應式數據變為深度只讀。
let data = reactive({name:'cat',age:10,hobby:{sleep:'day',walk:'night',}})let cat = readonly(data)
shallowReadonly
讓一個響應式數據變成淺只讀。
第一層數據只可讀,第二層及之后的數據可讀可改。
readonly和shallowReadonly應用場景:當數據完全或者一定程度上不希望被修改時。
雖然在一開始定義數據時,數據創建的是響應式,初衷似乎是希望頁面對數據的修改發生響應,而后又設置只讀,不希望數據被修改,這種行為看起來有些令人迷惑,但在實際使用中,可能數據是其他人創建的,而目前我們不希望這個數據可寫。就可以使用readonly和shallowReadonly對數據進行限制。