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

