詳解vue2和vue3如何定義響應(yīng)式數(shù)據(jù)
響應(yīng)式原理
Vue2和Vue3的響應(yīng)式實現(xiàn)原理是不同的
Vue2
底層是通過es5
的Object.defineProperty
,使用Object.defineProperty()進(jìn)行數(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ù)的類型進(jìn)行不同處理)
- 在腳本里使用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. 進(jìn)行獲取數(shù)據(jù),較為麻煩
- 明明模板中只用到了 name 和 age,卻把整個 obj 進(jìn)行了導(dǎo)出,沒必要,性能浪費。
使用toRef
進(jìn)行修改,只需要將需要的屬性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-08Vue?element-ui?el-cascader?只能末級多選問題
這篇文章主要介紹了Vue?element-ui?el-cascader?只能末級多選問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09