vue3聲明響應(yīng)式狀態(tài)使用(含ref,reactive,toRef(),toRefs()等)
Vue 3 中的數(shù)據(jù)基于 JavaScript Proxy (代理) 實(shí)現(xiàn)響應(yīng)式
( vue2 中的數(shù)據(jù)通過(guò) Object.defineProperty()
方法和對(duì)數(shù)組變異方法的重寫,實(shí)現(xiàn)響應(yīng)式)
選項(xiàng)式 API
用 data 選項(xiàng)聲明響應(yīng)式狀態(tài),值為返回一個(gè)對(duì)象的函數(shù)。
- 在創(chuàng)建組件實(shí)例的時(shí)候會(huì)調(diào)用此函數(shù)
- 函數(shù)返回的對(duì)象會(huì)用響應(yīng)式系統(tǒng)進(jìn)行包裝
data() { return { count: 1 } },
注意事項(xiàng)
- data 的頂層屬性避免用
$
和_
開頭(Vue 在組件實(shí)例上暴露的內(nèi)置 API 使用 $ 作為前綴,也為內(nèi)部屬性保留 _ 前綴。) - 未在data中定義的組件實(shí)例新屬性,無(wú)法觸發(fā)響應(yīng)式更新。
- 始終通過(guò) this 來(lái)訪問(wèn)響應(yīng)式狀態(tài)
data() { return { someObject: {} } }, mounted() { const newObject = {} this.someObject = newObject console.log(newObject === this.someObject) // false }
這里原始的 newObject 不會(huì)變?yōu)轫憫?yīng)式。
組合式 API
方式一 ref 【推薦】
所有類型的數(shù)據(jù),都推薦使用 ref 聲明響應(yīng)式。
import { ref } from 'vue' // 定義一個(gè)初始值為數(shù)字 0 的響應(yīng)式變量 countRef const countRef = ref(0) // 得到一個(gè)帶有 .value 屬性的 ref 對(duì)象 console.log(countRef ) // { value: 0 } // 通過(guò) .value 訪問(wèn) ref 響應(yīng)式變量的值 console.log(countRef.value) // 0 // 通過(guò) .value 修改 ref 響應(yīng)式變量的值 countRef.value++
- 參數(shù)為響應(yīng)式變量的初始值(當(dāng)參數(shù)是一個(gè)對(duì)象時(shí),
ref()
會(huì)在內(nèi)部調(diào)用reactive()
) - 返回一個(gè)帶有 .value 屬性的 ref 對(duì)象
- 建議變量名以
Ref
為后綴,以便識(shí)別其是一個(gè) ref 對(duì)象 - 模板中使用時(shí),ref 對(duì)象會(huì)自動(dòng)解包,無(wú)需使用.value
<button @click="countRef++"> {{ countRef }} </button>
- ref 對(duì)象在作為響應(yīng)式對(duì)象的屬性被訪問(wèn)或修改時(shí)會(huì)自動(dòng)解包。
const countRef = ref(0) // reactive 的用法詳見下文 const state = reactive({ count:countRef }) console.log(state.count) // 0 ,無(wú)需寫成 state.count.value state.count = 1 // 無(wú)需寫成 state.count.value console.log(countRef .value) // 1
- ref 的另一個(gè)功能是獲取模板引用(即 vue2 中的 this.$refs)
<script setup> import { ref, onMounted } from 'vue' // 獲取到 ref 屬性為 input 的模板元素的引用(ref 變量名必須和模板里的 ref 屬性值相同) const input = ref(null) // 生命周期:DOM 掛載完成 onMounted(() => { // input 輸入框獲得焦點(diǎn)(注意此處需使用.value) input.value.focus() }) </script> <template> <!-- ref 屬性為 input 的模板元素 --> <input ref="input" /> </template>
- 為什么需要 ref
因?yàn)?vue3 的響應(yīng)式是通過(guò) proxy 實(shí)現(xiàn)的,但 proxy 只能給對(duì)象添加響應(yīng)式,無(wú)法給值類型的數(shù)據(jù)添加響應(yīng)式,所以需要通過(guò) ref() 函數(shù)先將值類型的數(shù)據(jù)包裝成一個(gè)帶 value 屬性的 ref 對(duì)象,才能實(shí)現(xiàn)值類型數(shù)據(jù)的響應(yīng)式。
- 為什么需要 .value
因?yàn)?ref() 函數(shù)返回的是一個(gè) ref 對(duì)象,響應(yīng)式變量的值存儲(chǔ)在 ref 對(duì)象的value 屬性中,所以在 js 中讀取和修改響應(yīng)式變量的值的時(shí)候,都需要 .value ,但在以下情況中,為了方便使用,vue 對(duì) ref 對(duì)象進(jìn)行了自動(dòng)解包,所以無(wú)需 .value
- 模板中使用 ref 對(duì)象
- ref 對(duì)象作為響應(yīng)式對(duì)象的屬性被訪問(wèn)或修改
TS 中給 ref 聲明類型
- ref 會(huì)根據(jù)初始化時(shí)的值推導(dǎo)其類型
// 推導(dǎo)出的類型:Ref<number> const year = ref(2020)
- 但比較復(fù)雜的數(shù)據(jù)類型,需用 Ref
import { ref } from 'vue' import type { Ref } from 'vue' interface userInfo { id: number name: String } const userList: Ref<userInfo[]> = ref([])
方式二 reactive
用于創(chuàng)建對(duì)象類型數(shù)據(jù)的響應(yīng)式。
import { reactive } from 'vue' const state = reactive({ count: 0 })
<button @click="state.count++"> {{ state.count }} </button>
- reactive() 無(wú)需對(duì)源數(shù)據(jù)添加類似 ref 的包裝,可使對(duì)象本身具有響應(yīng)性
- reactive() 返回的是一個(gè)原始對(duì)象的 Proxy,它和原始對(duì)象是不相等的
const raw = {} const proxy = reactive(raw) // 代理對(duì)象和原始對(duì)象不是全等的 console.log(proxy === raw) // false
reactive() 的局限性
- 只能用于對(duì)象類型數(shù)據(jù) (對(duì)象、數(shù)組和如 Map、Set 這樣的集合類型),不能用于 string、number 或 boolean 這樣的值類型數(shù)據(jù)
- 替換整個(gè)對(duì)象會(huì)丟失響應(yīng)式(Vue 的響應(yīng)式跟蹤是通過(guò)屬性訪問(wèn)實(shí)現(xiàn)的),所以只能通過(guò)修改屬性來(lái)修改 reactive() 創(chuàng)建的響應(yīng)式變量。
- 解構(gòu)會(huì)丟失響應(yīng)式
const state = reactive({ count: 0 }) // 當(dāng)解構(gòu)時(shí),count 已經(jīng)與 state.count 斷開連接 let { count } = state // 不會(huì)影響原始的 state count++
其他相關(guān) API
vue 提供了專門的API 來(lái)實(shí)現(xiàn)在不丟失響應(yīng)式的情況下,解構(gòu)響應(yīng)式對(duì)象(reactive 創(chuàng)建),它們不創(chuàng)造響應(yīng)式 (如 ref , reactive),而是延續(xù)響應(yīng)式 (toRef , toRefs) !
toRef()
基于響應(yīng)式對(duì)象上的屬性,創(chuàng)建 ref (與源屬性保持同步)
- 第一個(gè)參數(shù)為 響應(yīng)式對(duì)象(reactive 創(chuàng)建)
- 第二個(gè)參數(shù)為 屬性名
const state = reactive({ foo: 1 }) // 基于響應(yīng)式對(duì)象 state 上的屬性 foo ,創(chuàng)建 ref const fooRef = toRef(state, 'foo') // 更改該 ref 會(huì)更新源屬性 fooRef.value++ console.log(state.foo) // 2 // 更改源屬性也會(huì)更新該 ref state.foo++ console.log(fooRef.value) // 3
vue3.3+ 新增了將值、refs 或 getters 規(guī)范化為 refs 的功能,詳見官網(wǎng)
https://cn.vuejs.org/api/reactivity-utilities.html#toref
toRefs()
將響應(yīng)式對(duì)象轉(zhuǎn)換為每個(gè)屬性都指向源對(duì)象相應(yīng)屬性的 ref 的普通對(duì)象
- 推薦用法:合成函數(shù)返回響應(yīng)式對(duì)象時(shí),使用 toRefs,以便解構(gòu)賦值時(shí)不丟失響應(yīng)式。(詳見下文的范例)
const state = reactive({ foo: 1, bar: 2 }) // 得到一個(gè)每個(gè)屬性都是 ref 的普通對(duì)象 const stateAsRefs = toRefs(state) /* stateAsRefs 的類型:{ foo: Ref<number>, bar: Ref<number> } */ // 源屬性改變,Refs 對(duì)象里的 ref 屬性會(huì)同步改變 state.foo++ console.log(stateAsRefs.foo.value) // 2 // Refs 對(duì)象里的 ref 屬性改變,源屬性會(huì)同步改變 stateAsRefs.foo.value++ console.log(state.foo) // 3
應(yīng)用范例:解構(gòu)對(duì)象屬性,簡(jiǎn)化模板引用
<!-- 組合式API --> <script setup> import { reactive, toRefs } from "vue"; function getMyInfo() { const me = reactive({ name: "朝陽(yáng)", age: 35, }); // 使用 toRefs 避免丟失響應(yīng)式 return toRefs(me); } // 通過(guò)解構(gòu)賦值,便于模板中使用 let { name, age } = getMyInfo(); const changeName = () => { name.value = "張三"; }; </script> <template> <p>姓名:{{ name }}</p> <p>年齡:{{ age }}</p> <button @click="changeName">改名</button> </template>
isRef()
判斷是否為 ref
let age = ref(35) if (isRef(age)) { }
isReactive()
判斷一個(gè)對(duì)象是否是由 reactive() 或 shallowReactive() 創(chuàng)建的代理。
isProxy()
判斷一個(gè)對(duì)象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 創(chuàng)建的代理。
unref()
對(duì) ref 對(duì)象解包,是 isRef(val) ? val.value : val
的語(yǔ)法糖
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue中style設(shè)置scoped后部分樣式不生效的解決
這篇文章主要介紹了vue中style設(shè)置scoped后部分樣式不生效的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-09-09vue數(shù)據(jù)對(duì)象length屬性未定義問(wèn)題
這篇文章主要介紹了vue數(shù)據(jù)對(duì)象length屬性未定義問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06Vue中監(jiān)視屬性和計(jì)算屬性區(qū)別解析
這篇文章主要介紹了Vue中監(jiān)視屬性和計(jì)算屬性區(qū)別,通過(guò)本文學(xué)習(xí)大家知道computed與watch配置項(xiàng)問(wèn)題,computed能完成的功能,watch都可以完成,本文通過(guò)實(shí)例代碼給大家詳細(xì)講解,需要的朋友可以參考下2022-10-10Vue3項(xiàng)目中預(yù)覽并打印PDF的兩種方法
最近在項(xiàng)目開發(fā)中碰到一個(gè)需求是在頁(yè)面中展示pdf預(yù)覽功能,下面這篇文章主要給大家介紹了關(guān)于Vue3項(xiàng)目中預(yù)覽并打印PDF的兩種方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05Vuex模塊化實(shí)現(xiàn)待辦事項(xiàng)的狀態(tài)管理
本文主要介紹了Vuex模塊化實(shí)現(xiàn)待辦事項(xiàng)的狀態(tài)管理的相關(guān)知識(shí),具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-03-03vue?實(shí)現(xiàn)列表跳轉(zhuǎn)至詳情且能添加至購(gòu)物車功能
列表頁(yè)面顯示數(shù)據(jù),點(diǎn)擊跳轉(zhuǎn)到對(duì)應(yīng)的詳情頁(yè),詳情頁(yè)可以添加并跳轉(zhuǎn)到購(gòu)物車,購(gòu)物車具有常見功能,這篇文章主要介紹了vue?實(shí)現(xiàn)列表跳轉(zhuǎn)至詳情且能添加至購(gòu)物車,需要的朋友可以參考下2022-10-10