Vue組合式API--setup中定義響應(yīng)式數(shù)據(jù)的示例詳解
一、Options API(選項(xiàng)式API)的弊端
1.1 什么是選項(xiàng)式API
在Vue2.x中,編寫組件的方式是使用Options API,它的特點(diǎn)是在對(duì)應(yīng)的屬性中編寫對(duì)應(yīng)的功能模塊。
比如data中定義數(shù)據(jù)、methods中定義方法、computed中定義計(jì)算屬性、watch中監(jiān)聽屬性改變。
生命周期鉤子也屬于Options API(選項(xiàng)式API)。
1.2 選項(xiàng)式API的弊端
- 當(dāng)我們實(shí)現(xiàn)某一個(gè)功能時(shí),這個(gè)功能對(duì)應(yīng)的代碼邏輯會(huì)被拆分到各個(gè)屬性中。
當(dāng)組件變得很大、很復(fù)雜時(shí),邏輯關(guān)注點(diǎn)的列表就會(huì)增長(zhǎng),那么同一個(gè)功能的邏輯就會(huì)被拆分的很分散。
特別是對(duì)于那些一開始沒有編寫這些組件的人(閱讀組件的人)來說,這個(gè)組件的代碼是難以閱讀和理解的。
- 碎片化的代碼使用理解和維護(hù)這個(gè)復(fù)雜的組件變得非常困難,很容易隱藏了潛在的邏輯問題。
- 當(dāng)處理單個(gè)邏輯關(guān)注點(diǎn)時(shí),需要不斷的跳到相應(yīng)的代碼塊中,增加了代碼維護(hù)的成本。
二、Composition API(組合式API)概述
2.1 Composition API的作用
如果能將同一個(gè)邏輯關(guān)注點(diǎn)相關(guān)的代碼收集在一起會(huì)更好一些,這就是Composition API想 要做的事情。
Vue Composition API可以簡(jiǎn)稱為VCA。
2.2 Composition API的編碼位置
Vue3.x提供了setup函數(shù)來編寫各種組合式API。
setup其實(shí)也是組件的另外一個(gè)選項(xiàng),只不過這個(gè)選項(xiàng)可以用來替代大部分其他選項(xiàng)式API選項(xiàng)。
比如methods、computed、watch、data、各種生命周期鉤子函數(shù)等等。
三、setup函數(shù)的參數(shù)和返回值
3.1 setup函數(shù)的參數(shù)
setup函數(shù)主要由兩個(gè)參數(shù):
props:父組件傳遞過來的屬性會(huì)被放到props對(duì)象中props的類型,和選項(xiàng)式API中的的規(guī)則一樣,在props選項(xiàng)中定義props的使用,在template中依然是可以把props中的屬性當(dāng)成普通Vue變量去使用。- 在
setup函數(shù)中用props,不可以通過this去獲取
context:也稱之為SetupContext,是setup函數(shù)的上下文對(duì)象,它主要由三個(gè)屬性attrs:所有的非props的的屬性slots:父組件傳遞過來的插槽,在以渲染函數(shù)返回時(shí)會(huì)有作用emit:當(dāng)組件內(nèi)部需要發(fā)出事件時(shí)會(huì)用到emit- 主要是因?yàn)?code>setup函數(shù)中不能訪問
this,所以無法通過this.$emit發(fā)出事件
3.2 setup函數(shù)的返回值
setup的返回值可以來替代data選項(xiàng),因此setup的返回值可以在模板template中使用。
<script>
import { ref } from 'vue'
export default {
setup() {
// 定義counter的內(nèi)容
// 默認(rèn)定義的數(shù)據(jù)都不是響應(yīng)式數(shù)據(jù),使用ref可以讓數(shù)據(jù)變成響應(yīng)式的
let counter = ref(100)
return {counter}
}
}
</script>
setup的返回值可以來替代methods中定義的方法
<script>
import { ref } from 'vue'
export default {
setup() {
// 定義counter的內(nèi)容
// 默認(rèn)定義的數(shù)據(jù)都不是響應(yīng)式數(shù)據(jù),使用ref可以讓數(shù)據(jù)變成響應(yīng)式的
let counter = ref(100)
const increment = () => {
counter.value ++
console.log(counter.value)
}
return {counter, increment}
}
}
</script>
四、setup中如何定義響應(yīng)式數(shù)據(jù)
4.1 reactive函數(shù)
代碼示例:
<template>
<div>
<h2>賬號(hào): {{ account.username }}</h2>
<h2>密碼: {{ account.password }}</h2>
<!-- 沒使用reactive函數(shù)時(shí),點(diǎn)擊此按鈕,能夠修改數(shù)據(jù),但是卻不能渲染到頁面上 -->
<button @click="changeAccount">修改賬號(hào)</button>
</div>
</template>
<script>
// 1.引入reactive函數(shù)
import { reactive } from 'vue'
export default {
setup() {
// 2.使用reactive函數(shù): 定義復(fù)雜類型的響應(yīng)式數(shù)據(jù)數(shù)據(jù)
const account = reactive({
username: "張三",
password: "666666"
})
function changeAccount() {
account.username = "kobe"
}
// 3.返回定義的數(shù)據(jù)
return {account, changeAccount}
}
}
</script>
由代碼示例可知,使用reactive函數(shù)注意好三步即可:
- 從
Vue中引入reactive函數(shù) - 把要定義的復(fù)雜數(shù)據(jù)類型(對(duì)象或者數(shù)組)傳入
reactive函數(shù),reactive函數(shù)的返回值就是響應(yīng)式數(shù)據(jù)不要 - 忘記在
setup函數(shù)中返回才可以在模板上使用
為什么加了reactive函數(shù)后數(shù)據(jù)就變成了響應(yīng)式的:
當(dāng)我們使用reactive函數(shù)處理我們的數(shù)據(jù)之后,數(shù)據(jù)再次被使用時(shí)就會(huì)進(jìn)行依賴收集。
當(dāng)數(shù)據(jù)發(fā)生改變時(shí),所有收集到的依賴都是進(jìn)行對(duì)應(yīng)的響應(yīng)式操作。
選項(xiàng)式API中的data選項(xiàng)中的復(fù)雜數(shù)據(jù),在底層也是交給了reactive函數(shù)將其編程響應(yīng)式對(duì)象的。
4.2 ref函數(shù)
1)ref函數(shù)的基本使用
reactive函數(shù)對(duì)傳入的類型是有限制的,它要求必須傳入的是一個(gè)對(duì)象或者數(shù)組類型。
如果傳入一個(gè)基本數(shù)據(jù)類型,比如String、Number、Boolean等等就會(huì)報(bào)出一個(gè)警告:
value cannot be made reactive : xxx
Vue3給我們提供了另外一個(gè)ref函數(shù)。
ref函數(shù)返回可變的響應(yīng)式對(duì)象,該對(duì)象作為一個(gè) 響應(yīng)式的引用維護(hù)著它內(nèi)部的值,這也是ref名稱的來源。
它內(nèi)部的值是在ref的value屬性中被維護(hù)的,因此使用ref函數(shù)定義的變量,使用時(shí)需要從value屬性取值。
代碼示例:
<template>
<div>
<!-- 默認(rèn)情況下在template中使用ref時(shí), vue會(huì)自動(dòng)對(duì)其進(jìn)行解包(取出其中value) -->
<h2>當(dāng)前計(jì)數(shù): {{ counter }}</h2>
<button @click="increment">+1</button>
<button @click="counter++">+1</button>
</div>
</template>
<script>
// 1.引入ref函數(shù)
import { ref } from 'vue'
export default {
setup() {
// 2.ref函數(shù): 主要定義簡(jiǎn)單類型的數(shù)據(jù)(也可以定義復(fù)雜類型的數(shù)據(jù))
const counter = ref(0)
function increment() {
counter.value++
}
// 3.返回定義的數(shù)據(jù)
return {counter, increment}
}
}
</script>
2)ref淺層解包
<template>
<div>
<!--ref 淺層解包 (意思就是把ref變量放別的對(duì)象中時(shí)就會(huì)有下面兩個(gè)情況)-->
<!-- 使用的時(shí)候不需要寫.value -->
<h2>當(dāng)前計(jì)數(shù): {{ info.counter }}</h2>
<!-- 修改的時(shí)候需要寫.value -->
<button @click="info.counter.value++">+1</button>
</div>
</template>
<script>
// 1.引入ref函數(shù)
import { ref } from 'vue'
export default {
setup() {
// 2.ref函數(shù): 主要定義簡(jiǎn)單類型的數(shù)據(jù)(也可以定義復(fù)雜類型的數(shù)據(jù))
const counter = ref(0)
const info = {counter}
// 3.返回定義的數(shù)據(jù)
return {counter}
}
}
</script>
注意點(diǎn):
- 在模板中引入
ref函數(shù)定義的值時(shí),Vue會(huì)自動(dòng)幫助我們進(jìn)行解包操作 - 解包就是
Vue自動(dòng)取出ref函數(shù)所定義的變量中的value值 - 所以使用者并不需要在模板中通過
ref.value的方式來使用 - 在
setup函數(shù)內(nèi)部,ref函數(shù)定義的值是一個(gè)ref引用, 所以需要使用ref.value的方式去操作 - 關(guān)于
ref的淺層解包 - 直接在
template中使用ref變量,會(huì)進(jìn)行自動(dòng)解包 - 如果把
ref變量放在一個(gè)對(duì)象當(dāng)中會(huì)有下面的情況- 使用時(shí),會(huì)進(jìn)行自動(dòng)解包
- 修改時(shí),不會(huì)進(jìn)行自動(dòng)解包,因此需要使用
value屬性去獲取值
4.3 關(guān)于如何選擇reactive函數(shù)和ref函數(shù)
1)選擇reactive函數(shù)的場(chǎng)景
reactive函數(shù)應(yīng)用于本地的數(shù)據(jù)的定義,本地?cái)?shù)據(jù)代表這個(gè)數(shù)據(jù)并不是來源于服務(wù)器reactive函數(shù)定義的復(fù)雜數(shù)據(jù)的多個(gè)屬性之間是有一定聯(lián)系的
import {reactive} from 'vue'
const user = reactive({
username: '張三',
password: '123456'
})
例如收集一個(gè)表單中的數(shù)據(jù),就是很適合使用reactive函數(shù)的場(chǎng)景
2)選擇ref函數(shù)的場(chǎng)景
定義本地的簡(jiǎn)單類型的數(shù)據(jù)
import {ref} from 'vue'
const message = ref('hello ref')
定義從服務(wù)器中獲取的數(shù)據(jù)
import {ref} from 'vue'
const music = ref([]) // 定義一個(gè)本地?cái)?shù)組
const serverMusicList = ['小星星', '蟲兒飛'] // 模擬從服務(wù)器獲取到的數(shù)據(jù)
music.value = serverMusicList // 把數(shù)據(jù)賦值給music變量
這是經(jīng)驗(yàn)之談,很多優(yōu)秀的開源項(xiàng)目中也都是這么取做的,文檔中并沒有明確說明什么情況使用什么
這其實(shí)也是程序員本身應(yīng)該自己去考慮的問題
五、單向數(shù)據(jù)流和readonly的使用
5.1 單向數(shù)據(jù)流規(guī)范
1)什么是單向數(shù)據(jù)流規(guī)范
父組件傳遞給子組件的對(duì)象數(shù)據(jù),子組件只能使用,不允許修改。
雖然是能修改的,但是違反了單向數(shù)據(jù)流的原則。
如果真的想修改這個(gè)數(shù)據(jù),應(yīng)該發(fā)出事件,讓父組件監(jiān)聽這個(gè)事件,然后在父組件中進(jìn)行修改。
2)為什么要遵循這個(gè)原則
今后項(xiàng)目中可能會(huì)有很多子組件,父組件的一個(gè)數(shù)據(jù)可能會(huì)傳遞給多個(gè)子組件。
由于傳遞的是數(shù)據(jù)對(duì)象的引用,因此其中一個(gè)子組件中修改了數(shù)據(jù),會(huì)導(dǎo)致全部引用它的地方,全部受到影響。
而且維護(hù)時(shí),難以知道是哪個(gè)位置修改的這個(gè)數(shù)據(jù)。
其它地方也有很多類似的概念規(guī)范:
react中react有一個(gè)重要的原則:任何一個(gè)組件都應(yīng)該像純函數(shù)一樣,不能修改傳入的propsjs中- 在
js中也有類似原則,熟悉js的可能會(huì)知道一個(gè)純函數(shù)的概念。純函數(shù)意思就是函數(shù)不要對(duì)傳入的參數(shù)進(jìn)行修改。
這些規(guī)范如果不遵守,代碼確實(shí)也能實(shí)現(xiàn)功能。但是將來維護(hù)性將會(huì)非常差,誰試誰知道。
5.2 readonly的使用
在給組件傳遞數(shù)據(jù)時(shí),如果希望組件使用傳遞的內(nèi)容。但是不允許它們修改時(shí),就可以使用readonly了。
因?yàn)閱蜗驍?shù)據(jù)流原則在Vue2.x的時(shí)候時(shí)沒法從代碼上避免的。如果一個(gè)程序員根本不知道這個(gè)原則。
就有可能寫出難以維護(hù)的代碼。
而Vue3.x則提供了readonly函數(shù)從編碼層面去避免這個(gè)問題。
<template>
<!-- 給組件home傳遞一個(gè)只讀數(shù)據(jù) -->
<home :info="readonlyInfo" />
</template>
<script>
import { readonly } from 'vue'
setup() {
// 本地定義多個(gè)需要傳遞給子組件的數(shù)據(jù)
const info = reactive({
name: "張三",
age: 25,
height: 1.78
})
// 使用readOnly包裹info
const roInfo = readonly(info)
return {roInfo}
}
</script>
readonly返回的對(duì)象都是不允許修改的;但是經(jīng)過readonly處理的原來的對(duì)象是允許被修改的。
比如 對(duì)于語句const info = readonly(obj),info對(duì)象是不允許被修改的。
當(dāng)obj被修改時(shí),readonly返回的info對(duì)象也會(huì)被修改;
但是我們不能去修改readonly返回的對(duì)象info;
如果代碼本身就非常遵守單向數(shù)據(jù)流的原則,那么也可以不使用
readonly的其它注意點(diǎn):
readonly會(huì)返回原始對(duì)象的只讀代理- 也就是它依然是一個(gè)
Proxy,這是一個(gè)proxy的set方法被劫持,并且不能對(duì)其進(jìn)行修改 - 在開發(fā)中常見的
readonly方法會(huì)傳入三個(gè)類型的參數(shù): - 類型一:普通對(duì)象
- 類型二:
reactive返回的對(duì)象 - 類型三:
ref的對(duì)象
六、Reactive相關(guān)的判斷的API
isProxy:檢查對(duì)象是否是由reactive或readonly創(chuàng)建的proxyisReactive: 檢查對(duì)象是否是由reactive創(chuàng)建的響應(yīng)式代理- 如果該代理是
readonly建的,但包裹了由reactive創(chuàng)建的另一個(gè)代理,它也會(huì)返回true isReadonly:檢查對(duì)象是否是由readonly創(chuàng)建的只讀代理。toRaw:返回reactive或readonly代理的原始對(duì)象(不建議保留對(duì)原始對(duì)象的持久引用。請(qǐng)謹(jǐn)慎使用)shallowReactive: 創(chuàng)建一個(gè)響應(yīng)式代理,它跟蹤其自身property的響應(yīng)性
但不執(zhí)行嵌套對(duì)象的深層響應(yīng)式轉(zhuǎn)換 (深層還是原生對(duì)象)。
shallowReadonly: 創(chuàng)建一個(gè)proxy,使其自身的property為只讀
但不執(zhí)行嵌套對(duì)象的深度只讀轉(zhuǎn)換(深層還是可讀、可寫的)
這些用的可能不會(huì)很多,但是也要了解
七、ref其他的API
7.1 toRefs
使用ES6的解構(gòu)語法,對(duì)reactive返回的對(duì)象進(jìn)行解構(gòu)獲取值,數(shù)據(jù)將不再是響應(yīng)式的。
const info = reactive({
name: "張三",
age: 25,
height: 1.78
})
ES6解構(gòu):
const {name, age, height} = info;
數(shù)據(jù)失去響應(yīng)式
toRefs解構(gòu):
const {name, age, height} = toRefs(info);解構(gòu)后數(shù)據(jù)仍然保持響應(yīng)式
7.2 unref
用于獲取一個(gè)ref引用中的value。
- 如果參數(shù)是一個(gè)
ref,則返回內(nèi)部值,否則返回參數(shù)本身 - 這也是
val = isRef(val) ? val.value : val的語法糖函數(shù) 7.3 isRef
判斷值是否是一個(gè)ref對(duì)象。
7.4 shallowRef
創(chuàng)建一個(gè)淺層的ref對(duì)象。
7.5 triggerRef
手動(dòng)觸發(fā)和shallowRef創(chuàng)建對(duì)象的響應(yīng)式。
到此這篇關(guān)于Vue組合式API--setup中定義響應(yīng)式數(shù)據(jù)詳解的文章就介紹到這了,更多相關(guān)Vue組合式API setup內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue獲取子組件實(shí)例對(duì)象ref屬性的方法推薦
在Vue中我們可以使用ref屬性來獲取子組件的實(shí)例對(duì)象,這個(gè)功能非常方便,這篇文章主要給大家介紹了關(guān)于Vue獲取子組件實(shí)例對(duì)象ref屬性的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06
Vue使用正則校驗(yàn)文本框?yàn)檎麛?shù)
這篇文章主要介紹了Vue使用正則校驗(yàn)文本框?yàn)檎麛?shù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Vant中List組件immediate-check=false無效的解決
這篇文章主要介紹了Vant中List組件immediate-check=false無效的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01

