組合式API
介紹
什么是組合式 API(composition API)
- 組合式 API 是 Vue3 提供的新功能永淌,為我們提供了另一種編寫(xiě)組件的方式
- 代碼共享。在 setup 鉤子里面佩耳,可以按邏輯上的關(guān)注點(diǎn)來(lái)對(duì)代碼進(jìn)行分組遂蛀,并與其他組件共享代碼。
組合式 API 的出現(xiàn)解決了什么樣的問(wèn)題
- 隨著Vue項(xiàng)目規(guī)模和復(fù)雜性的增加干厚,現(xiàn)有 Option API 變得難以應(yīng)付
- 邏輯并沒(méi)有真正地按功能分組李滴,這可能會(huì)讓人很難讀懂一個(gè)龐大而復(fù)雜的組件文件。
- 通過(guò) Option API 實(shí)現(xiàn)組件間共享重復(fù)的邏輯(共享代碼)比較麻煩
- 通過(guò)組合時(shí) API 我們可以將重用邏輯封裝為功能蛮瞄,這個(gè)功能可以組件間共享所坯,這樣你就可以在整個(gè)應(yīng)用程序的不同組件中使用這些邏輯。
接口
setup
執(zhí)行時(shí)間
優(yōu)先于beforeCreate 和 created挂捅。setup 選項(xiàng)在組件創(chuàng)建之前執(zhí)行芹助,一旦 props 被解析,就將作為組合式 API 的入口籍凝。
參數(shù)
- props:組件傳入的屬性
- context
setup中接受的props是響應(yīng)式的周瞎,當(dāng)傳入新的props會(huì)被及時(shí)更新苗缩。在setup中不能直接訪問(wèn)到this對(duì)象饵蒂。
所以在setup中定義props,用這樣的方式
defineProps({
msg: String
})
context是一個(gè)普通的JavaScript對(duì)象酱讶,它暴露組件的三個(gè)property
context.attrs, context.slots, context.emit
組合式API生命周期函數(shù)
組合式api生命周期
onBeforeMount
onMounted
onBeforeUpdate
onUpdated
onUnmounted ( -> destroyed)
onActivated ( -> activited)
onDeacitivated ( ->deacitivated)
onRenderTriggered
onRenderTricked
reactive 和 ref
reactive 和 ref的作用都是定義和跟蹤需要雙向綁定的變量退盯。也就是數(shù)據(jù)定義,將一個(gè)普通對(duì)象變?yōu)轫憫?yīng)式對(duì)象泻肯。
ref 接收參數(shù)并將其包裹在一個(gè)帶有 value property 的對(duì)象中返回渊迁,然后可以使用該 property 訪問(wèn)或更改響應(yīng)式變量的值
toRefs的作用就是,將一個(gè)reactive對(duì)象轉(zhuǎn)化為屬性全部為ref對(duì)象的普通對(duì)象
<template>
<div> {{ month }}</div>
</template>
<script>
import {ref,reactive,toRefs } from "vue"
export default defineComponent({
setup() {
const year = ref(0)
const date = reactive({
month: 1
minutes:200
})
return {
year,
...toRefs(date)
}
}
)
</script>
isRef
isRef用于判斷變量是否為ref對(duì)象
const unwrapped = isRef(foo) ? foo.value : foo
computed
computed函數(shù)與vue2中computed功能一致灶挟,它接收一個(gè)函數(shù)并返回一個(gè)value為getter返回值的不可改變的響應(yīng)式ref對(duì)象琉朽。
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 錯(cuò)誤,computed不可改變
// 同樣支持set和get屬性
onst count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => { count.value = val - 1 }
})
plusOne.value = 1
console.log(count.value) // 0
watch
用法
- 指定依賴(lài)源:
在基礎(chǔ)用法中稚铣,
當(dāng)依賴(lài)了多個(gè)reactive或ref是無(wú)法直接看到的箱叁,需要在回調(diào)中尋找.
<template>
<div>
<input type="text" v-model="state.count">{{state.count}}
<input type="text" v-model="inputRef">{{inputRef}}
</div>
</template>
<script>
import {watch, reactive, ref} from 'vue'
export default {
setup() {
const state = reactive({count: 0})
const inputRef = ref('')
// state.count與inputRef中任意一個(gè)源改變都會(huì)觸發(fā)watch
watch(() => {
console.log('state', state.count)
console.log('ref', inputRef.value)
})
return {state, inputRef}
}
}
</script>
- 指定依賴(lài)源
<template>
<div>
state2.count: <input type="text" v-model="state2.count">
{{state2.count}}<br/><br/>
ref2: <input type="text" v-model="ref2">{{ref2}}<br/><br/>
</div>
</template>
<script>
import {watch, reactive, ref} from 'vue'
export default {
setup() {
const state2 = reactive({count: ''})
const ref2 = ref('')
// 通過(guò)函數(shù)參數(shù)指定reative依賴(lài)源
// 只有在state2.count改變時(shí)才會(huì)觸發(fā)watch
watch(
() => state2.count,
() => {
console.log('state2.count',state2.count)
console.log('ref2.value',ref2.value)
})
// 直接指定ref依賴(lài)源
watch(ref2,() => {
console.log('state2.count',state2.count)
console.log('ref2.value',ref2.value)
})
return {state, inputRef, state2, ref2}
}
}
</script>
- 指定多個(gè)數(shù)據(jù)源
<template>
<div>
<p>
<input type="text" v-model="state.a"><br/><br/>
<input type="text" v-model="state.b"><br/><br/>
</p>
<p>
<input type="text" v-model="ref1"><br/><br/>
<input type="text" v-model="ref2"><br/><br/>
</p>
</div>
</template>
<script>
import {reactive, ref, watch} from 'vue'
export default {
setup() {
const state = reactive({a: 'a', b: 'b'})
// state.a和state.b任意一個(gè)改變都會(huì)觸發(fā)watch的回調(diào)
watch(() => [state.a, state.b],
// 回調(diào)的第二個(gè)參數(shù)是對(duì)應(yīng)上一個(gè)狀態(tài)的值
([a, b], [preA, preB]) => {
console.log('callback params:', a, b, preA, preB)
console.log('state.a', state.a)
console.log('state.b', state.b)
console.log('****************')
})
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)
console.log('##############')
})
return {state, ref1, ref2}
}
}
</script>
- 取消watch
<template>
<div>
<input type="text" v-model="inputRef">
<button @click="onClick">stop</button>
</div>
</template>
<script>
import {watch, ref} from 'vue'
export default {
setup() {
const inputRef = ref('')
const stop = watch(() => {
console.log('watch', inputRef.value)
})
const onClick = () => {
// 取消watch墅垮,取消之后對(duì)應(yīng)的watch不會(huì)再執(zhí)行
stop()
}
return {inputRef, onClick}
}
}
</script>
-
onCleanyp函數(shù):watch提供了一個(gè)onCleanup的副作用清除函數(shù),該函數(shù)接收一個(gè)函數(shù)耕漱,在該函數(shù)中進(jìn)行副作用清除算色。那么onCleanup什么時(shí)候執(zhí)行?
- watch的callback即將被第二次執(zhí)行時(shí)先執(zhí)行onCleanup螟够。
- watch被停止時(shí)灾梦,即組件被卸載之后。
-watch選項(xiàng)
deep
深層對(duì)象的任意一個(gè)屬性的改變都會(huì)出發(fā)回調(diào)的執(zhí)行
<template>
<div>
<button @click="onClick">CHANGE</button>
</div>
</template>
<script>
import {watch, ref} from 'vue'
export default {
setup() {
const objRef = ref({a: {b: 123}, c: 123})
watch(objRef,() => {
console.log('objRef.value.a.b',objRef.value.a.b)
}, {deep: true})
const onClick = () => {
// 設(shè)置了deep之后妓笙,深層對(duì)象任意屬性的改變都會(huì)觸發(fā)watch回調(diào)的執(zhí)行
objRef.value.a.b = 780
}
return {onClick}
}
}
</script>