詳解vue2和vue3如何定義響應(yīng)式數(shù)據(jù)
響應(yīng)式原理
Vue2和Vue3的響應(yīng)式實現(xiàn)原理是不同的
Vue2底層是通過es5的Object.defineProperty,使用Object.defineProperty()進行數(shù)據(jù)劫持,結(jié)合訂閱發(fā)布的方式實現(xiàn),有一定的局限性。Vue3底層是通過es6的Porxy, 通過Proxy代理,使用ref或者reactive將數(shù)據(jù)轉(zhuǎn)化為響應(yīng)式數(shù)據(jù),能夠更好地支持動態(tài)添加屬性和刪除屬性。它解決了Vue2底層實現(xiàn)的缺點,對數(shù)組、層級比較深的對象處理都很優(yōu)秀,但缺點是瀏覽器兼容不是很好。
Vue2的響應(yīng)式數(shù)據(jù)
在Vue2中,是使用選項式API的方式來編寫代碼,比如data()、computed()、watch等方法實現(xiàn)響應(yīng)式
Vue3的響應(yīng)式數(shù)據(jù)
reactive和ref
reactive
reactive定義引用數(shù)據(jù)類型(以對象和數(shù)組舉例),它能夠?qū)?fù)雜數(shù)據(jù)類型的內(nèi)部屬性或者數(shù)據(jù)項聲明為響應(yīng)式數(shù)據(jù),所以reactive的響應(yīng)式是深層次的,其底層是通過ES6的Proxy來實現(xiàn)數(shù)據(jù)響應(yīng)式,相對于Vue2的Object.defineProperty,具有能監(jiān)聽增刪操作,能監(jiān)聽對象屬性的變化等優(yōu)點
- reactive是一個函數(shù),它可以定義一個復(fù)雜數(shù)據(jù)類型,成為響應(yīng)式數(shù)據(jù);
- 通常用來定義響應(yīng)式的對象數(shù)據(jù)。
<template>
<div>name:{{obj.name}}</div>
<button @click="updateName">修改name</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup() {
// 定義響應(yīng)式對象
const obj = reactive({
name: 'lisi',
age:20
})
const updateName = () => {
obj.name = '我是修改后的name'
console.log('我是按鈕..........');
};
return {
obj,
updateName
}
}
}
</script>
ref函數(shù)
ref函數(shù),常用于簡單數(shù)據(jù)類型定義為響應(yīng)式數(shù)據(jù),其實也可以定義復(fù)雜數(shù)據(jù)類型的響應(yīng)式數(shù)據(jù),對于數(shù)據(jù)未知的情況下ref是最適用的。
在修改值,獲取值的時候,需要.value。在模板中使用ref申明的響應(yīng)式數(shù)據(jù),可以省略.value,在js代碼中修改ref聲明的數(shù)據(jù),需要加上.value。
<template>
<div>name:{{name}}</div>
<button @click="changeName">修改name</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const name = ref('zhangsan');
const changeName = () => {
name.value = 'lisi'
}
return {
name,
changeName
}
}
}
</script>
兩者的不同
- ref用于定義基本類型和引用類型,reactive僅用于定義引用類型
- reactive只能用于定義引用數(shù)據(jù)類型的原因在于內(nèi)部是通過ES6的Proxy實現(xiàn)響應(yīng)式的,而Proxy不適用于基本數(shù)據(jù)類型
- ref定義對象時,底層會通過reactive轉(zhuǎn)換成具有深層次的響應(yīng)式對象,所以ref本質(zhì)上是reactive的再封裝(會判斷數(shù)據(jù)的類型進行不同處理)
- 在腳本里使用ref定義的數(shù)據(jù)時,記得加.value后綴
- 在定義數(shù)組時,建議使用ref,從而可避免reactive定義時值修改導(dǎo)致的響應(yīng)式丟失問題
const tableData = reactive([]) // 定義
const getTableData = async () => {
const { data } = await getTableDataApi() // 模擬接口獲取表格數(shù)據(jù)
tableData = data // 修改,錯誤示例,這樣賦值會使tableData失去響應(yīng)式
}
// 方法一:改為 ref 定義
const tableData = ref([])
const getTableData = async () => {
const { data } = await getTableDataApi()
tableData.value = data // 使用.value重新賦值
}
// 方法二:使用 push 方法
const tableData = reactive([])
const getTableData = async () => {
const { data } = await getTableDataApi()
tableData.push(...data) // 先使用...將data解構(gòu),再使用push方法
}
// 方法三:定義時數(shù)組外層嵌套一個對象
const tableData = reactive({ list:[] })
const getTableData = async () => {
const { data } = await getTableDataApi()
tableData.list = data // 通過訪問list屬性重新賦值
}
// 方法四:賦值前再包一層 reactive
const tableData = reactive([])
const getTableData = async () => {
const { data } = await getTableDataApi()
tableData = reactive(data) // 賦值前再包一層reactive
}為什么需要兩個
雖然ref函數(shù)既可以處理基本數(shù)據(jù)類型也可以處理引用數(shù)據(jù)類型,但是在普通js代碼里修改該響應(yīng)式數(shù)據(jù)的值時需要使用.value的寫法,會存在.value的嵌套問題,因此使用reactive來處理引用數(shù)據(jù)類型,避免該問題。
toRef和toRefs
ref是對元數(shù)據(jù)的拷貝,修改響應(yīng)式數(shù)據(jù)時不會影響之前的數(shù)據(jù),視圖會更新。tooRef和toRefs是對元數(shù)據(jù)的引用,修改響應(yīng)式數(shù)據(jù)時,原數(shù)據(jù)也會改變,但是視圖不會更新,只有原始數(shù)據(jù)改變后,該數(shù)據(jù)和視圖都會更新。toRef修改的是對象的某個屬性,toRefs修改的是整個對象
toRef
toRef 函數(shù)的作用:轉(zhuǎn)換響應(yīng)式對象中某個屬性為單獨響應(yīng)式數(shù)據(jù),并且轉(zhuǎn)換后的值和之前是關(guān)聯(lián)的(ref 函數(shù)也可以轉(zhuǎn)換,但值非關(guān)聯(lián))。
<template>
<div class="container">
<h2>name: {{ obj.name }} age: {{obj.age}}</h2>
<button @click="updateName">修改數(shù)據(jù)</button>
</div></template><script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
const obj = reactive({
name: '初映',
age: 18,
address: '江西',
sex: '男',
})
const updateName = () => {
obj.name = '初映CY的前說'
}
return { obj, updateName }
},
}</script>
這樣寫有幾個問題:
- 模板中都要使用 obj. 進行獲取數(shù)據(jù),較為麻煩
- 明明模板中只用到了 name 和 age,卻把整個 obj 進行了導(dǎo)出,沒必要,性能浪費。
使用toRef進行修改,只需要將需要的屬性return出去即好,且模板中也不需要加obj.前綴了。
<template>
<div class="container">
<h2>name: {{ name }} </h2>
<button @click="updateName">修改數(shù)據(jù)</button>
</div></template><script>
import { reactive,toRef } from 'vue'
export default {
name: 'App',
setup() {
const obj = reactive({
name: '初映',
age: 18,
address: '江西',
sex: '男',
})
const name = toRef(obj, 'name')
const updateName = () => {
obj.name = '初映CY的前說'
}
return { name, updateName }
},
}</script>
toRefs
toRefs 函數(shù)的作用:轉(zhuǎn)換響應(yīng)式對象中所有屬性為單獨響應(yīng)式數(shù)據(jù),并且轉(zhuǎn)換后的值和之前是關(guān)聯(lián)的。
<template>
<div>{{name}}</div>
<div>{{age}}</div>
<button @click="update">修改name</button>
</template>
<script>
import {reactive, toRefs} from 'vue'
export default {
setup() {
const obj = reactive({
name: '張三',
age:18
})
console.log(obj);
const obj2 = toRefs(obj);
console.log(obj2); //發(fā)現(xiàn)obj2里面的name和age都是響應(yīng)式屬性,指向obj的屬性
// 解構(gòu)之后重新賦值的是普通對象
const obj3 = { ...obj };
console.log(obj3);
const update =()=> {
obj.name = '我是修改的原始數(shù)據(jù)的obj'
}
return {
// 解構(gòu)obj2,用的時候直接拿屬性名,不需要obj2.name或obj2.age
...obj2,
update
}
}
}
</script>
修改原始數(shù)據(jù)obj后會發(fā)現(xiàn),轉(zhuǎn)換過后的obj2中的值會跟著改變。
以上就是詳解vue2和vue3如何定義響應(yīng)式數(shù)據(jù)的詳細(xì)內(nèi)容,更多關(guān)于vue定義響應(yīng)式數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決vue2.0 element-ui中el-upload的before-upload方法返回false時submit(
這篇文章主要介紹了vue2.0 element-ui中el-upload的before-upload方法返回false時submit()不生效的解決方法,這里需要主要項目中用的element-ui是V1.4.3,感興趣的朋友參考下吧2018-08-08
Vue?element-ui?el-cascader?只能末級多選問題
這篇文章主要介紹了Vue?element-ui?el-cascader?只能末級多選問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09

