Vue組合式API--setup中定義響應(yīng)式數(shù)據(jù)的示例詳解
一、Options API(選項(xiàng)式API)的弊端
1.1 什么是選項(xiàng)式API
在Vue2.x
中,編寫組件的方式是使用Options API
,它的特點(diǎn)是在對應(yīng)的屬性中編寫對應(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è)功能對應(yīng)的代碼邏輯會(huì)被拆分到各個(gè)屬性中。
當(dāng)組件變得很大、很復(fù)雜時(shí),邏輯關(guān)注點(diǎn)的列表就會(huì)增長,那么同一個(gè)功能的邏輯就會(huì)被拆分的很分散。
特別是對于那些一開始沒有編寫這些組件的人(閱讀組件的人)來說,這個(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
可以簡稱為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
對象中props
的類型,和選項(xiàng)式API
中的的規(guī)則一樣,在props選項(xiàng)中定義props
的使用,在template
中依然是可以把props
中的屬性當(dāng)成普通Vue
變量去使用。- 在
setup
函數(shù)中用props
,不可以通過this
去獲取
context
:也稱之為SetupContext
,是setup
函數(shù)的上下文對象,它主要由三個(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>賬號: {{ account.username }}</h2> <h2>密碼: {{ account.password }}</h2> <!-- 沒使用reactive函數(shù)時(shí),點(diǎn)擊此按鈕,能夠修改數(shù)據(jù),但是卻不能渲染到頁面上 --> <button @click="changeAccount">修改賬號</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ù)類型(對象或者數(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)行對應(yīng)的響應(yīng)式操作。
選項(xiàng)式API
中的data
選項(xiàng)中的復(fù)雜數(shù)據(jù),在底層也是交給了reactive
函數(shù)將其編程響應(yīng)式對象的。
4.2 ref函數(shù)
1)ref函數(shù)的基本使用
reactive
函數(shù)對傳入的類型是有限制的,它要求必須傳入的是一個(gè)對象或者數(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)式對象,該對象作為一個(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)對其進(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ù): 主要定義簡單類型的數(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變量放別的對象中時(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ù): 主要定義簡單類型的數(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è)對象當(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ù)的場景
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ù)的場景
2)選擇ref函數(shù)的場景
定義本地的簡單類型的數(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ī)范
父組件傳遞給子組件的對象數(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ù)對象的引用,因此其中一個(gè)子組件中修改了數(shù)據(jù),會(huì)導(dǎo)致全部引用它的地方,全部受到影響。
而且維護(hù)時(shí),難以知道是哪個(gè)位置修改的這個(gè)數(shù)據(jù)。
其它地方也有很多類似的概念規(guī)范:
react
中react
有一個(gè)重要的原則:任何一個(gè)組件都應(yīng)該像純函數(shù)一樣,不能修改傳入的props
js
中- 在
js
中也有類似原則,熟悉js
的可能會(huì)知道一個(gè)純函數(shù)的概念。純函數(shù)意思就是函數(shù)不要對傳入的參數(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
返回的對象都是不允許修改的;但是經(jīng)過readonly
處理的原來的對象是允許被修改的。
比如 對于語句const info = readonly(obj)
,info
對象是不允許被修改的。
當(dāng)obj
被修改時(shí),readonly
返回的info
對象也會(huì)被修改;
但是我們不能去修改readonly
返回的對象info
;
如果代碼本身就非常遵守單向數(shù)據(jù)流的原則,那么也可以不使用
readonly的其它注意點(diǎn):
readonly
會(huì)返回原始對象的只讀代理- 也就是它依然是一個(gè)
Proxy
,這是一個(gè)proxy
的set
方法被劫持,并且不能對其進(jìn)行修改 - 在開發(fā)中常見的
readonly
方法會(huì)傳入三個(gè)類型的參數(shù): - 類型一:普通對象
- 類型二:
reactive
返回的對象 - 類型三:
ref
的對象
六、Reactive相關(guān)的判斷的API
isProxy
:檢查對象是否是由reactive
或readonly
創(chuàng)建的proxy
isReactive
: 檢查對象是否是由reactive
創(chuàng)建的響應(yīng)式代理- 如果該代理是
readonly
建的,但包裹了由reactive
創(chuàng)建的另一個(gè)代理,它也會(huì)返回true
isReadonly
:檢查對象是否是由readonly
創(chuàng)建的只讀代理。toRaw
:返回reactive
或readonly
代理的原始對象(不建議保留對原始對象的持久引用。請謹(jǐn)慎使用)shallowReactive
: 創(chuàng)建一個(gè)響應(yīng)式代理,它跟蹤其自身property
的響應(yīng)性
但不執(zhí)行嵌套對象的深層響應(yīng)式轉(zhuǎn)換 (深層還是原生對象)。
shallowReadonly
: 創(chuàng)建一個(gè)proxy
,使其自身的property
為只讀
但不執(zhí)行嵌套對象的深度只讀轉(zhuǎn)換(深層還是可讀、可寫的)
這些用的可能不會(huì)很多,但是也要了解
七、ref其他的API
7.1 toRefs
使用ES6
的解構(gòu)語法,對reactive
返回的對象進(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對象。
7.4 shallowRef
創(chuàng)建一個(gè)淺層的ref
對象。
7.5 triggerRef
手動(dòng)觸發(fā)和shallowRef
創(chuàng)建對象的響應(yīng)式。
到此這篇關(guān)于Vue組合式API--setup中定義響應(yīng)式數(shù)據(jù)詳解的文章就介紹到這了,更多相關(guān)Vue組合式API setup內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue使用正則校驗(yàn)文本框?yàn)檎麛?shù)
這篇文章主要介紹了Vue使用正則校驗(yàn)文本框?yàn)檎麛?shù)問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Vant中List組件immediate-check=false無效的解決
這篇文章主要介紹了Vant中List組件immediate-check=false無效的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01