1. 拉開序幕的 setup
理解:vue3 中的一個(gè)新的配置項(xiàng)勾笆,值為一個(gè)函數(shù)橡类。
setup 是所有 Compositon api 的表演的舞臺(tái)。
組件中用到的:數(shù)據(jù)摆碉,方法塘匣,watch,計(jì)算屬性等都要配置在 setup 中
setup 的返回值有兩種:
- 若返回一個(gè)對(duì)象巷帝,則對(duì)象中的屬性忌卤,方法,在模板中均可以直接使用楞泼。(重點(diǎn)關(guān)注)
- 若返回的是一個(gè)渲染函數(shù)驰徊,則可以直接定義渲染內(nèi)容。(了解)
- 注意點(diǎn):
盡量不要與 vue2 混用堕阔。
-- vue2配置(data, methods, computed...)中可以訪問到 setup 中的屬性棍厂,方法。但是setup 中不能訪問到 vue2 中的配置(data, methods, computed...)
-- 如果有重名超陆,setup 優(yōu)先setup 不能是一個(gè) async 函數(shù)牺弹,因?yàn)榉祷刂挡辉偈?return 的對(duì)象,而是 promise, 模板看不到 returen 對(duì)象中的屬性例驹。
2. ref函數(shù)
- 作用:定義一個(gè)響應(yīng)式的數(shù)據(jù)捐韩。
- 語法: const xxx = ref(initeValue)
- 創(chuàng)建一個(gè)包含響應(yīng)式數(shù)據(jù)的引用對(duì)象(reference 對(duì)象)。
- JS 中操作數(shù)據(jù): xxx.value
- 模板中讀取不用 .vulue 直接寫鹃锈。
- 備注:
- 接收數(shù)據(jù)可以是是基本數(shù)據(jù)類型荤胁,也可以是對(duì)象類型。
- 基本數(shù)據(jù)類型:響應(yīng)依然是靠 Object.defineProperty() 里的 get 和 set 完成的屎债。
- 對(duì)象類型:內(nèi)部是求助了 vue3 中的內(nèi)置的新函數(shù) reactive 函數(shù)仅政。
3. reactive 函數(shù)
- 作用:定義一個(gè)對(duì)象類型的響應(yīng)式∨杈裕基本理性用 ref 函數(shù)圆丹。
- 語法: const 代理對(duì)象 = reactive(原對(duì)象)。 接收一個(gè)對(duì)象(或者數(shù)據(jù))躯喇,返回一個(gè)代理對(duì)象(proxy 對(duì)象)辫封。
- reactive 定義的響應(yīng)式數(shù)據(jù)是深層次的。
- 內(nèi)部基于 ES6 的 proxy 實(shí)現(xiàn)廉丽,通過代理對(duì)象操作源對(duì)象內(nèi)部數(shù)據(jù)倦微,進(jìn)行操作。
4. vue2 的響應(yīng)式原理
- 實(shí)現(xiàn)原理:
- 對(duì)象類型:通過 Object.defineProperty() 對(duì)屬性的讀取正压,修改進(jìn)行攔截(數(shù)據(jù)劫持)欣福。
- 數(shù)據(jù)類型:通過重寫更新數(shù)組的一系列方法來實(shí)現(xiàn)攔截. (對(duì)數(shù)組的變更方法進(jìn)行了包裹)。
Object.defineProperty(data,'count',{
set(){},
get(){}
})
- 存在問題:
- 新增屬性焦履,刪除屬性拓劝,界面不會(huì)發(fā)生變化
- 數(shù)組通過下標(biāo)修改,不會(huì)更新界面
5. vue3 的響應(yīng)式
- 實(shí)現(xiàn)原理:
- 通過 Proxy(代理):攔截對(duì)象中任意屬性的變化嘉裤,包括屬性的讀寫郑临,添加,刪除等屑宠。
- 通過 Reflect (反射):實(shí)現(xiàn)源對(duì)象的屬性進(jìn)行操作
- MDN 有對(duì)應(yīng)的文檔
new Proxy(data, {
//攔截讀取數(shù)據(jù)屬性值
get(target, prop){
return Reflect.get(target,prop)
},
//攔截設(shè)置或或添加屬性值
set(target,prop,value){
return Reflect.set(target,prop,value)
}
//攔截刪除屬性
deleteProperty(target, prop){
return Reflect.deleteProperty(target, prop)
}
})
6. ref 和 reactive 的對(duì)比
- 從定義數(shù)據(jù)角度對(duì)比
- ref 定義基本類型數(shù)據(jù)
- reactive 定義對(duì)象或者數(shù)組類型數(shù)據(jù)
- 備注:ref 也可以定義對(duì)象或者數(shù)組類型數(shù)據(jù)牧抵,但是內(nèi)部它自動(dòng)通過 reactive 轉(zhuǎn)為代理對(duì)象實(shí)現(xiàn)。
- 從原理角度對(duì)比:
ref 還是通過 vue2 的 Object.defineProperty() 的 get 和 set 實(shí)現(xiàn)的響應(yīng)式(數(shù)據(jù)劫持)侨把。
reactive 通過使用 Proxy 來實(shí)現(xiàn)響應(yīng)式(數(shù)據(jù)劫持),并通過 Reflect 操作源對(duì)象內(nèi)部的數(shù)據(jù)妹孙。
- 從使用角度對(duì)比:
ref 定義的數(shù)據(jù):操作數(shù)據(jù)需要 .value秋柄。 讀取數(shù)據(jù)時(shí)候模板中直接讀取不需要 .value
reactive 定義的數(shù)據(jù),操作與讀取均不需要 .value
7. setup 的兩個(gè)注意點(diǎn)
setup 的執(zhí)行時(shí)機(jī)
在 beforeCreate 之前執(zhí)行一次蠢正,this 是 undifinedsetup 的參數(shù)
- props:值為對(duì)象骇笔,包含:組件外部傳遞過來,且組件內(nèi)部聲明接收了的屬性。
- context:上下文對(duì)象
-- attrs:值為對(duì)象笨触,包含:組件外部傳遞過來懦傍,但是沒有在 props 配置中-聲明的屬性。相當(dāng)于 this.slots.
-- emit: 分發(fā)自定義函數(shù)粗俱,相當(dāng)于 this.$emit.
8. 計(jì)算屬性與監(jiān)視
8.1 computed 函數(shù)
- 與vue2 中的 computed 相比,配置功能一致虚吟。
- 寫法
import {computed} from 'vue'
setup(){
...
//計(jì)算屬性一般寫法
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//計(jì)算屬性完整寫法
let fullName = computed(()=>{
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.splice("-")
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
8.2 watch 函數(shù)
- 與 vue2 中的 watch 配置一樣
- 兩個(gè)小坑
- 監(jiān)視 reactive 定義的響應(yīng)式數(shù)據(jù)時(shí)寸认,oldValue 無法正確獲取,強(qiáng)制開啟了深度監(jiān)視(deep 配置失效)串慰。
- 監(jiān)視 reactive 定義的響應(yīng)式數(shù)據(jù)中的某個(gè)屬性時(shí)候偏塞,deep 配置有效。
//情況一邦鲫,監(jiān)視 ref定義的數(shù)據(jù)
watch(sum, (newValue,oldVaule)=>{
consolo.log("sum數(shù)據(jù)變化了", newValue,oldVaule)
})
//情況二灸叼,監(jiān)視多個(gè) ref定義的數(shù)據(jù)
watch([sum,msg], (newValue,oldVaule)=>{
consolo.log("sum或者 msg 數(shù)據(jù)變化了", newValue,oldVaule)
})
//情況三,監(jiān)視r(shí)eactive定義的數(shù)據(jù)
//若 watch 監(jiān)視的是 reactive 定義的響應(yīng)式數(shù)據(jù)庆捺,則無法獲取正確的 oldValue
//若 watch 監(jiān)視的是 reactive 定義的響應(yīng)式數(shù)據(jù)古今,則強(qiáng)制開啟了深度監(jiān)視
watch(person, (newValue,oldVaule)=>{
consolo.log("person數(shù)據(jù)變化了", newValue,oldVaule)
}, {immediate:ture, deep: false}) //此處的 deep 不生效
//情況四,監(jiān)視r(shí)eactive定義的某個(gè)屬性
watch(()=>person.job, (newValue,oldVaule)=>{
consolo.log("person的job數(shù)據(jù)變化了", newValue,oldVaule)
})
//情況五疼燥,監(jiān)視r(shí)eactive定義的某些屬性
watch([()=>person.job,()=>person.name], (newValue,oldVaule)=>{
consolo.log("person的job或者name數(shù)據(jù)變化了", newValue,oldVaule)
})
//特殊情況
watch([()=>person.job,()=>person.name], (newValue,oldVaule)=>{
consolo.log("person的job或者name數(shù)據(jù)變化了", newValue,oldVaule)
}, {deep:true}) //此處由于監(jiān)視的是 reactive 定義中的某個(gè)屬性沧卢,所以 deep配置有效。
8.3 watchEffect 函數(shù)
- watch 函數(shù)的套路:既要指明監(jiān)視的屬性醉者,也要指明監(jiān)視的回調(diào)
- watchEffect 函數(shù)的套路是:不用指明監(jiān)視那個(gè)屬性但狭,監(jiān)視的回調(diào)中用到那個(gè)屬性,那就監(jiān)視那個(gè)屬性撬即。
- watchEffect有點(diǎn)像 computed:
- 但是 computed 注重的計(jì)算出來的值(回調(diào)函數(shù)返回值)立磁,所以必須要寫返回值。
- 而 watchEffect 更注重的是過程(回調(diào)函數(shù)的函數(shù)體)剥槐,所以不用寫返回值唱歧。
//watchEffect所指定的回調(diào)中用到的數(shù)據(jù)只要發(fā)生變化,則直接重新執(zhí)行回調(diào)
watchEffect(()=>{
const x = sum.value
const x2 = person.age
console.log("watchEffect配置的回調(diào)執(zhí)行了")
})