常用的 Composition API(組合API)
setup
- Vue3.0新增的配置項划提,值為一個函數(shù)
- setup是所有Composition API的
- 組件中所用到的:數(shù)據(jù)王浴,方法等等丑蛤,均需要配置在setup函數(shù)中
- setup函數(shù)的兩種返回值:
- 若返回是一個對象,對象中的屬性重绷,方法唬渗,在模板中均可以直接使用
- 若返回一個渲染函數(shù),則可以自定義渲染內(nèi)容
- setup的兩個注意點:
- setup的執(zhí)行時機:在beforeCreate之前執(zhí)行一次圃泡,this是undefined
- setup的參數(shù):
- props:值為對象,包含外部傳遞過來愿险,且組件內(nèi)部申明接收了的屬性
- context:上下文對象
- attrs:值為對象颇蜡,包含組件外部傳遞過來,但是沒有在props中配置中申明的屬性辆亏,相當于this.$attrs
- slots:收到的插槽的內(nèi)容风秤,相當于this.$slots
- emit:分發(fā)自定義時間的函數(shù),相當于this.$emit扮叨。(值得注意的是缤弦,Vue3中使用emit,需要使用emits去接收父組件傳過來的數(shù)據(jù)彻磁,和props
差不多)
PS:
- 盡量不要與Vue2的配置項混用:
- Vue2配置項(data,methods,computed...)中可以訪問到setup中的屬性碍沐,方法。
- 但在setup中不能訪問到Vue2中的配置
- 如果有重名兵迅,setup優(yōu)先
- setup函數(shù)不能是一個async函數(shù)(后面學習異步組件抢韭,也行薪贫,需要的是Suspense和異步組件引入的配合)恍箭,因為返回值不在是一個return的對象,而是promise瞧省,模板看不到return對象中的屬性
<template>
<h1>{{name}}</h1>
<button @click="sayHi"></button>
</template>
<script>
exprot default {
name: 'App',
setup(props,context) {
const name = "景天" // 不是響應式的數(shù)據(jù)
function sayHi() {
console.log(`Hi,我是${name}`)
}
// 返回值是一個對象
return {
name,
sayHi
}
// 返回值是一個渲染函數(shù)
return () => h('h1','這是渲染函數(shù)渲染的內(nèi)容')
}
}
</script>
ref函數(shù)
- 作用:定義一個響應式的數(shù)據(jù)扯夭,或者說是生成一個引用實現(xiàn)對象
- 語法:
const xxx = ref(initValue)
- 創(chuàng)建一個包含響應式數(shù)據(jù)的引用對象(reference對象)
- JS中操作數(shù)據(jù)需要:xxx.value
- 模板中讀取數(shù)據(jù):直接使用即可
PS:
- ref接收的數(shù)據(jù)可以是基本類型數(shù)據(jù),也可以是對象類型
- 基本類型的數(shù)據(jù):響應式是靠Object鞍匾。defineProperty()的get和set完成的
- 對象類型的數(shù)據(jù):內(nèi)部使用了reactive函數(shù)
<template>
<h1>{{name}}</h1>
<h1>{{age}}</h1>
<button @click="sayHi"></button>
</template>
<script>
import {ref} from 'vue'
exprot default {
name: 'App',
setup() {
const name = ref("景天")
const age = ref(18)
const job = ref({
type: '前端'交洗,
money:'30K'
}) // 其實Vue3幫你調(diào)用了reactive函數(shù)
// 在setup中訪問ref定義的基本數(shù)據(jù)類型的數(shù)據(jù),需要 .value才能拿到值
function sayHi() {
name.value = '飛蓬'
age.value = '1000'
job.value.money = '60K'
}
return {
name,
age,
job,
sayHi
}
}
}
</script>
reactive函數(shù)
- 作用:定義一個對象類型的響應式數(shù)據(jù)
- 語法:
const 代理對象 = reactive(源對象)
參數(shù)是一個對象或者數(shù)組橡淑,返回一個代理對象(Proxy的實例對象构拳,簡稱proxy對象) - reactive定義的響應式數(shù)據(jù)是深層次的
- 內(nèi)部基于ES6的Proxy實現(xiàn),通過代理對象操作源對象內(nèi)部數(shù)據(jù)
<template>
<h1>{{name}}</h1>
<h1>{{age}}</h1>
<button @click="sayHi"></button>
</template>
<script>
import {ref, reactive} from 'vue'
exprot default {
name: 'App',
setup() {
const name = ref("景天")
const age = ref(18)
const job = reactive({
type: '前端'梁棠,
money:'30K'
})
const hobby = ['抽煙', '喝酒', '燙頭']
function sayHi() {
name.value = '飛蓬'
age.value = '1000'
job.money = '60K' // 不需要.value了
hobby[0] = '吃飯'
}
return {
name,
age,
job,
hobby,
sayHi
}
// ref定義的基本類型的數(shù)據(jù)都要.value取值置森,有點煩,我們可以換一種思路符糊,而且語義化
const person = reactive({
name: '景天',
age: 18,
jon: {
type: '前端'凫海,
money:'30K'
},
hobby: ['抽煙', '喝酒', '燙頭']
})
return {
person
}
}
}
</script>
computed
簡寫和完整版寫法和Vue2都一樣,只是使用不一樣男娄,需要單獨引入使用行贪,變成了一個函數(shù)
<template>
<h1><input v-model="firstName" /></h1>
<h1><input v-model="lastName" /></h1>
<div>{{fullName}}</div>
</template>
<script>
import {reactive,computed} from 'vue'
exprot default {
name: 'App',
setup() {
const person = reactive({
firstName + : '景天',
lastName: 18,
})
// 或者可以綁定在person對象上
// person.fullName = computed(()=>{
let fullName = computed(()=>{
// 簡寫形式漾稀,完整版的get和set和Vue2都一樣
return person.firstName + person.lastName
})
return {
person,
fullName
}
}
}
</script>
watch
- 和Vue2基本相似,配置是一樣的
- 兩個坑:
- 監(jiān)視reactive定義的響應式數(shù)據(jù)時:oldVal無法正確的獲取建瘫,強制開啟了深度監(jiān)視(deep配置無效)
- 監(jiān)視reactive定義的響應式數(shù)據(jù)中的某個屬性(對象)時:deep配置有效
<template>
<h1>{{num}}</h>
<h1>{{msg}}</h>
</template>
<script>
import {ref,reactive,watch} from 'vue'
exprot default {
name: 'App',
setup() {
let num = ref(0)
let msg= ref('消息')
let p = reactive({
name: '景天',
age: 18,
a: {
b: {
c: 100
}
}
})
// 1. 監(jiān)視ref所定義的一個響應式數(shù)據(jù)
watch(num,(newVal,oldVal)=> {
console.log(newVal,oldVal)
},{immediate:true})
// 2. 監(jiān)視ref所定義的多個響應式數(shù)據(jù)
watch([num,msg],(newVal,oldVal)=> {
console.log(newVal,oldVal)
},{immediate:true})
// 3. 監(jiān)視reactive所定義的一個響應式數(shù)據(jù)崭捍。
// 注意:此處無法正確的獲得oldVal
// 注意:強制開啟了深度監(jiān)視器(deep配置無效)
watch(p,(newVal,oldVal)=> {
console.log(newVal,oldVal)
},{deep:false}) // 此處的deep無效
// 4. 監(jiān)視reactive所定義的一個響應式數(shù)據(jù)中的某個屬性。需要使用函數(shù)
watch(()=>p.name,(newVal,oldVal)=> {
console.log(newVal,oldVal)
})
// 5. 監(jiān)視reactive所定義的一個響應式數(shù)據(jù)中的某些屬性啰脚。
watch([()=>p.name,()=>p.age],(newVal,oldVal)=> {
console.log(newVal,oldVal)
},{deep:false})
// 特殊情況
watch(()=>p.a,(newVal,oldVal)=> {
console.log(newVal,oldVal)
},{deep:true}) // 此處由于監(jiān)視的是reactive所定義的對象中的某個屬性(對象)缕贡,所以deep配置有效
return {
num,
msg,
p
}
}
}
</script>
watchEffect函數(shù)
- watch:需要指明監(jiān)視的屬性,也要指明監(jiān)視的回調(diào)
- watchEffect:不需要指明監(jiān)視哪個豎向拣播,監(jiān)視的回調(diào)中用到哪個屬性晾咪,就監(jiān)視那個屬性
- watchEffect有點類似于computed:
- computed注重的是計算出來的值,所以必須寫返回值
- watchEffect更注重的是過程贮配,所以不需要寫返回值
<template>
<h1>{{num}}</h>
<h1>{{msg}}</h>
</template>
<script>
import {ref,reactive,watchEffect} from 'vue'
exprot default {
name: 'App',
setup() {
let num = ref(0)
let msg= ref('消息')
let p = reactive({
name: '景天',
age: 18,
a: {
b: {
c: 100
}
}
})
watchEffect(()=> {
const x1 = sum.value
const x2 = age.value
console.log('watchEffect調(diào)用了')
})
return {
num,
msg,
p
}
}
}
</script>
toRef
- 作用:創(chuàng)建一個ref對象谍倦,其value值指向另一個對象中的某個屬性,這樣才是響應式的
- 語法:
const name = toRef(person,'name')
- 應用:要將響應式對象中的某個屬性單獨提供給外部使用
- 擴展:toRefs和toRef功能一直泪勒,但是可以批量創(chuàng)建多個ref對象昼蛀,語法
toRefs(person)
// 上面說過數(shù)據(jù)可以定義reactive,避免.value圆存。但是這樣的話叼旋,我們使用數(shù)據(jù)就要person.name這樣使用,那么可以試試這樣
return {
...toRefs(person)
}
其他Composition API
shallowReactive和shallowRef
- shallowReactive:值處理對象最外層屬性的響應式(淺響應式)
- shallowRef:只處理基本數(shù)據(jù)類型的響應式沦辙,處理基本類型數(shù)據(jù)夫植,和ref沒有區(qū)別,不進行對象的響應式處理
- 什么時候使用油讯?
- 如果有一個對象數(shù)據(jù)详民,結(jié)構比較淺,但變化時只是外層屬性變化陌兑,使用shallowReactive
- 如果有一個對象數(shù)據(jù)沈跨,后續(xù)功能不會修改該對象中的屬性,而是生成新的對象來替換兔综,使用shallowRef
readonly和shallowReadonly
- readonly:讓一個響應式數(shù)據(jù)變?yōu)橹蛔x(深只讀)
- shallowReadonly:讓一個響應式數(shù)據(jù)變?yōu)橹蛔x(淺只讀)饿凛,只是最外層屬性值不讓改
- 使用場景:不希望數(shù)據(jù)被改變時,比如別人定義的數(shù)據(jù)软驰,你使用涧窒,但是改了對別人有影響,你在使用的時候可以先使用
readonly(person)
,這樣就不怕被改了碌宴。
toRaw和markRaw
- toRaw:
- 作用:將一個由reactive生成的響應式對象轉(zhuǎn)換為普通對象杀狡。
toRaw(person)
- 場景:用于讀取響應式對象對應的普通對象,對于這個普通對象的所有操作贰镣,不會引起頁面的更新
- 作用:將一個由reactive生成的響應式對象轉(zhuǎn)換為普通對象杀狡。
- markRaw
- 作用:標記一個對象呜象,使其永遠不會再成為響應式對象
- 場景:
- 有些值不應該被設置為響應式的膳凝,如第三方庫
- 當渲染具有不可變數(shù)據(jù)的大列表時,跳過響應式轉(zhuǎn)換可以提高性能
customRef
- 作用:創(chuàng)建一個自定義的ref恭陡,并對其依賴項跟蹤和更新觸發(fā)進行顯式控制蹬音。
demo:實現(xiàn)輸入數(shù)據(jù),防抖效果
<template>
<input v-model="keyword">
<div>{{keyword}}</div>
</template>
<script>
import { customRef } from 'vue'
export default {
name: 'demo',
setup() {
// 自定義一個ref休玩,名稱為myRef
function myRef(value,delay) {
let timer
// 使用vue的customRef去配置我們的myRef,參數(shù)為一個函數(shù)著淆,這個函數(shù)有兩個參數(shù)可以直接使用
// track:
// trigger:
return customRef((track,trigger)=> {
// 要求必須返回一個對象,里面包含get和set
return {
get() {
// 通知vue追蹤value的變化
track()
return value
},
set(neValue) {
clearTimeout(timer)
timer = setTimeout(()=> {
value = newValue
// 通知vue去重新解析模板
trigger()
},delay)
}
}
})
}
}
let keyword = myRef('Hello',100)
return {
keyword
}
}
</script>
provide和inject在Vue3中的使用
- 作用:實現(xiàn)祖孫組件間通信,即父組件定義拴疤,后面所有子代組件都能用
- 套路:父組件有一個provide選項來提供數(shù)據(jù)永部,后代組件有一個inject選項來開始使用這些數(shù)據(jù)
// 祖組件
import {reactive} from 'vue'
setup() {
let car = reactive({
name: '奧迪',
price: '100W'
})
provide('car',car)
}
// 后代組件
import {inject} from 'vue'
setup(props,context) {
const car = inject('car')
return {
car
}
}
響應式數(shù)據(jù)的判斷
- isRef: 檢查一個值是不是一個ref對象
- isReactive: 檢查一個對象是不是由reactive創(chuàng)建愛你的響應式代理
- isReadonly: 檢查一個對象是不是由readonly創(chuàng)建的只讀代理
- isProxy: 檢查一個對象是否是由ractive或者readonly方法創(chuàng)建的代理
Vue3中的新組件
Fragment
相當于一個虛擬的容器,在Vue中呐矾,每個組件苔埋,都需要有個根組件,但是在Vue3不需要蜒犯,其實是Vue3幫助我們調(diào)用了這個組件组橄,在我們的頁面上,不會渲染出來
Teleport
當我們的組件總有些內(nèi)容罚随,不想顯示在我們自己的組件中玉工,可以使用這個組件,比如我們有個彈窗淘菩,想顯示在body標簽中
// to這里支持標簽
// to支持css標簽
// <teleport to="body">
<teleport to="#main">
<div class="dialog">
這是一個對話框
</div>
</teleport>
Suspense
試驗性
Suspense 是一個試驗性的新特性遵班,其API可能隨時會發(fā)生變動。特此聲明瞄勾,以便社區(qū)能夠為當前的實現(xiàn)提供反饋费奸。
生產(chǎn)環(huán)境請勿使用。
以上是官方的警告进陡!
- 等待異步組件渲染一些額外的內(nèi)容,讓應用有更好的用戶體驗
-
<suspense>
組件有兩個插槽微服。它們都只接收一個直接子節(jié)點趾疚。default
插槽里的節(jié)點會盡可能展示出來。如果不能以蕴,則展示fallback
插槽里的節(jié)點糙麦。 - 這里就是之前說到setup的時候提到的,setup的返回值不能使用異步丛肮,但是如果像下面這樣使用異步函數(shù)引入子組件赡磅,那么子組件就可以使用異步了,像Promise宝与。
<template>
<div>
<Suspense>
// 這兩個插槽名稱是固定的
// defalut:這里面寫的就是我們正常要顯示的組件和代碼
// fallback:這里寫的就是應急代碼焚廊,就是正常代碼沒有顯示的時候的代碼
<template v-slot:defalut:>
<Child/>
</template>
<template v-slot:fallback>
<div>加載中...</div>
</template>
</Suspense>
</div>
</template>
<script>
// import Child from './component/Child.vue' // 平時使用的靜態(tài)引入
// 下面這是異步引入
import { defineAsyncComponent } from 'vue'
const Child = defineAsyncComponent(()=> import('./component/Child.vue'))
export default {
name: 'app',
components: {
Child
}
setup() {
}
}
</script>