深入探究Vue2響應(yīng)式原理的實(shí)現(xiàn)及存在的缺陷
一、Vue2中的響應(yīng)式原理
- Vue 2的響應(yīng)式原理:
在Vue 2中,響應(yīng)式是通過使用Object.defineProperty()方法來實(shí)現(xiàn)的。
在組件實(shí)例化過程中,Vue會對數(shù)據(jù)對象(data)進(jìn)行遞歸地遍歷,將每個屬性都轉(zhuǎn)換為getter/setter,并且為每個屬性創(chuàng)建一個依賴追蹤的系統(tǒng)。當(dāng)屬性被訪問或修改時,getter/setter會觸發(fā)依賴追蹤系統(tǒng),從而進(jìn)行依賴收集和派發(fā)更新,以保證數(shù)據(jù)和視圖的同步。 - 具體實(shí)現(xiàn)步驟如下:
1.創(chuàng)建Observer對象:通過遞歸地將data對象的屬性轉(zhuǎn)換為響應(yīng)式屬性,使用Object.defineProperty()為每個屬性添加getter和setter方法。Vue2中 通過使用 Object.defineProperty() 方法,將對象的屬性轉(zhuǎn)換成 getter 和 setter,當(dāng)數(shù)據(jù)發(fā)生變化時,會自動觸發(fā)相應(yīng)的更新函數(shù),實(shí)現(xiàn)數(shù)據(jù)的響應(yīng)式。
2.創(chuàng)建Dep對象:用來管理 Watcher,它用來收集依賴、刪除依賴和向依賴發(fā)送消息等。用于解耦屬性的依賴收集和派發(fā)更新操作。
3.創(chuàng)建Watcher對象:Watcher對象用于連接視圖和數(shù)據(jù)之間的橋梁,當(dāng)被依賴的屬性發(fā)生變化時,Watcher對象會接收到通知并更新視圖。當(dāng)數(shù)據(jù)發(fā)生變化時,它會通知訂閱該數(shù)據(jù)的組件更新視圖。Watcher 在實(shí)例化時會將自己添加到 Dep 中,當(dāng)數(shù)據(jù)發(fā)生變化時,會觸發(fā)相應(yīng)的更新函數(shù)。
4.模板編譯:Vue會解析模板,將模板中的數(shù)據(jù)綁定指令轉(zhuǎn)譯為對應(yīng)的更新函數(shù),以便在數(shù)據(jù)發(fā)生變化時調(diào)用。
在修改對象的值的時候,會觸發(fā)對應(yīng)的 setter, setter通知之前依賴收集得到的 Dep 中的Watcher,告訴它自己的值改變了,需要重新渲染視圖。這時候這些 Watcher就會開始調(diào)用 update 來更新視圖, 對應(yīng)的getter觸發(fā)追蹤,把新值重新渲染到視圖上
input用v-model綁定數(shù)據(jù),我們需要在input元素上添加事件監(jiān)聽,每當(dāng)input事件被觸發(fā)時,就修改對應(yīng)的data,data里的數(shù)據(jù)又會響應(yīng)式更新回視圖
二、模擬簡易版響應(yīng)式原理
實(shí)現(xiàn)思路:
定義一個Observe構(gòu)造函數(shù)用于對data對象的屬性進(jìn)行數(shù)據(jù)劫持。我們使用Object.defineProperty()方法對data對象的每個屬性進(jìn)行劫持,定義了屬性的getter和setter方法。
在getter方法中,我們返回屬性的值。在setter方法中,我們判斷新值是否與舊值不同,如果不同,則更新屬性的值,并觸發(fā)依賴更新。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <script type="text/javascript" > let data = { name:'前端百草閣', age:21, } function Observer(obj){ //匯總對象中所有的鍵形成一個數(shù)組 const keys = Object.keys(obj) //遍歷 keys.forEach((k)=>{ Object.defineProperty(this,k,{ get(){ return obj[k] }, set(val){ console.log(`${k}被改了,我要通知Vue重新去解析模板.....`) obj[k] = val } }) }) } //創(chuàng)建一個監(jiān)視的實(shí)例對象,用于監(jiān)視data中屬性的變化 const obs = new Observer(data) //準(zhǔn)備一個vm實(shí)例對象 let vm = {} vm._data = data = obs </script> </body> </html>
這個時候,原先data里的屬性就會各自有一個為他們服務(wù)的getter和setter,變成了具有響應(yīng)式的屬性
- 簡易式版本的缺陷
- 缺陷一:正常vue中會做一個數(shù)據(jù)代理,當(dāng)訪問vm.name時,訪問的其實(shí)是vm._data.name,這樣做了數(shù)據(jù)代理后使用起來更方便
- 缺陷二: 簡易式版本沒有考慮到data里面的屬性值還是對象的情況,在Vue中利用遞歸的方法將data里所有的屬性通過遞歸的方式都轉(zhuǎn)換為了響應(yīng)式屬性(即使屬性值是一個數(shù)組,數(shù)組里藏了對象,依然可以把對應(yīng)的屬性轉(zhuǎn)換為響應(yīng)式屬性)
這里有一個小tips,利用this指向obs,訪問this(obs)里的屬性,getter返回的其實(shí)是obj里的屬性(數(shù)據(jù)代理),為什么要這樣呢?如果說你訪問obj里的屬性,我真的通過getter給你返回了obj里對應(yīng)的屬性,返回的obj里的屬性又要去觸發(fā)自己的getter,那是不是就陷入死循環(huán)了呢?導(dǎo)致的問題就是無論你是觸發(fā)getter 還是setter都會導(dǎo)致超出最大調(diào)用堆棧這個錯誤
解決這個問題還有一個辦法就是利用閉包,利用閉包把初始值傳給value存起來了,后續(xù)getter和setter都是針對閉包內(nèi)的value,和原本的obj隔離開了,當(dāng)你訪問或者設(shè)置obj.key的時候,就會去修改對應(yīng)的val(由于閉包val不會被垃圾機(jī)制回收)就不存在最大調(diào)用堆棧溢出的情況了
function observe(obj) { if (!obj || typeof obj !== 'object') { return; } Object.keys(obj).forEach(function(key) { defineReactive(obj, key, obj[key]); }); } function defineReactive(obj, key, val) { observe(val); // 遞歸地對data對象的屬性進(jìn)行數(shù)據(jù)劫持 Object.defineProperty(obj, key, { get: function() { return val; }, set: function(newValue) { if (newValue !== val) { val = newValue; // 觸發(fā)依賴更新 updateView(); } } }); } function updateView() { document.querySelector('h1').innerText = vm.message; } // 初始化數(shù)據(jù)劫持 observe(vm.$data);
在上述代碼中,observe函數(shù)用于遞歸地對data對象的屬性進(jìn)行數(shù)據(jù)劫持。在defineReactive函數(shù)中,我們使用Object.defineProperty()方法對data對象的每個屬性進(jìn)行劫持,定義了屬性的getter和setter方法。
在getter方法中,我們返回屬性的值。在setter方法中,我們判斷新值是否與舊值不同,如果不同,則更新屬性的值,并觸發(fā)依賴更新。
最后,我們調(diào)用observe(vm.$data)來初始化數(shù)據(jù)劫持,使得Vue能夠捕獲到對data對象屬性的訪問和修改操作,并觸發(fā)相應(yīng)的依賴更新。
三、Vue2響應(yīng)式數(shù)據(jù)帶來的缺陷
Vue 2中的響應(yīng)式數(shù)據(jù)存在一些缺陷,但通過使用Vue提供的補(bǔ)救辦法,可以解決大部分響應(yīng)式數(shù)據(jù)的問題。
3.1 新增屬性的響應(yīng)問題
Vue在初始化時會對data對象的屬性進(jìn)行數(shù)據(jù)劫持,但是對于后續(xù)新增的屬性,Vue無法自動進(jìn)行響應(yīng)式處理。
Vue 無法探測普通的新增屬性 ,比如 this.myObject.saying = 'hi'
,這個新增的saying屬性是不具有響應(yīng)式的,Vue探測不到
3.2 數(shù)組變動的響應(yīng)問題
Vue對數(shù)組的變動(例如通過索引修改數(shù)組元素、通過splice方法刪除或插入元素)無法直接進(jìn)行響應(yīng)式處理。
例如此時在data里定義了這些數(shù)據(jù)
data:{ friends:[ {name:'jerry',age:35}, {name:'tony',age:36}, '前端百草閣' ] }
不難發(fā)現(xiàn)數(shù)組中的對象都是響應(yīng)式的,但數(shù)組中的普通元素卻不是響應(yīng)式的,意味著若直接修改數(shù)組中的元素Vue無法監(jiān)測到
如果你通過數(shù)組下標(biāo)修改對象屬性的話是可以監(jiān)測的,因?yàn)閷ο罄锏膶傩远际琼憫?yīng)式的,但如果你通過數(shù)組下標(biāo)修改普通元素是無法監(jiān)測到的
如果用一個新數(shù)組覆蓋掉原先的數(shù)組,Vue是能監(jiān)測到的
3.3 對象屬性的刪除問題
Vue無法直接檢測到對象屬性的刪除操作。利用delete刪除對象的屬性,無法被Vue監(jiān)測到
四、Vue2響應(yīng)式缺陷的解決辦法
4.1 新增屬性的響應(yīng)問題
Vue.set( target, propertyName/index, value )
向響應(yīng)式對象中添加一個 property,并確保這個新 property 同樣是響應(yīng)式的,且觸發(fā)視圖更新。它必須用于向響應(yīng)式對象上添加新 property,因?yàn)?Vue 無法探測普通的新增 property (比如 this.myObject.newProperty = ‘hi’)
給data中的student對象添加一個屬性,并且是響應(yīng)式的,有兩種寫法,Vue.set或者this.$set
// Vue.set(this._data.student,'sex','男') // 這里加不加_data實(shí)際上都可以,就是一個數(shù)據(jù)代理,訪問誰都一樣,那我們肯定選擇偷懶啦 this.$set(this.student,'sex','男') // this代表vm vue實(shí)例對象
實(shí)現(xiàn)了新增了student對象里的sex屬性,并且該屬性有為自己服務(wù)的getter、setter(具有響應(yīng)式)
但是,Vue官網(wǎng)明確指出:注意對象不能是 Vue 實(shí)例,或者 Vue 實(shí)例的根數(shù)據(jù)對象。
簡單來說就是,set方法的第一個參數(shù)target不允許 是vm(vue實(shí)例)、也不允許是vm._data(根數(shù)據(jù)對象)
4.2 數(shù)組變動的響應(yīng)問題
第一中解決辦法,使用數(shù)組變異方法:Vue提供了一些數(shù)組變異方法(例如push、pop、shift、unshift、splice、sort和reverse),這些方法會觸發(fā)數(shù)組的響應(yīng)式更新。
如果不是這七個方法的話,比如調(diào)用slice等數(shù)組方法的話,記得要把返回的新數(shù)組覆蓋掉原來的舊數(shù)組,依然能觸發(fā)響應(yīng)式
第二種解決辦法,利用set方法,set方法不但能解決對象新增屬性的問題,還能解決修改數(shù)組的問題(用的不多)
4.3 對象屬性的刪除問題
Vue.delete方法:用來刪除對象的屬性,并觸發(fā)響應(yīng)式更新。例如,可以使用Vue.delete(vm.someObject, ‘propertyToDelete’)來刪除一個屬性。
正常的delete方法,雖然確實(shí)刪除了屬性,但是無法被監(jiān)測到
利用Vue.delete完美解決刪除對象屬性無法被監(jiān)測的問題(很少用到),或者vm.$delete(vm.person,'name')
總結(jié)
Vue 2的響應(yīng)式數(shù)據(jù)機(jī)制在大多數(shù)情況下能夠滿足我們的需求,但也存在一些缺陷。
首先,Vue無法直接響應(yīng)新增的屬性,需要使用特定的方法進(jìn)行補(bǔ)救。其次,對于數(shù)組的變動和對象屬性的刪除,Vue也無法直接進(jìn)行響應(yīng)式處理,需要使用相應(yīng)的方法來觸發(fā)更新。這些缺陷在實(shí)際開發(fā)中可能會帶來一些困擾。
但幸運(yùn)的是,Vue提供了一些補(bǔ)救的辦法,如Vue.set和Vue.delete方法,以及數(shù)組變異方法。通過這些補(bǔ)救措施,我們可以彌補(bǔ)Vue 2響應(yīng)式數(shù)據(jù)機(jī)制的不足,提升開發(fā)效率和用戶體驗(yàn)。盡管如此,我們也期待Vue未來版本的改進(jìn),在響應(yīng)式數(shù)據(jù)方面能夠更加智能和靈活,以滿足更多復(fù)雜場景的需求。
以上就是深入探究Vue2響應(yīng)式原理的實(shí)現(xiàn)及存在的缺陷的詳細(xì)內(nèi)容,更多關(guān)于Vue2響應(yīng)式原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue3+ts重復(fù)參數(shù)提取成方法多處調(diào)用以及字段無值時不傳字段給后端問題
在進(jìn)行API開發(fā)時,優(yōu)化參數(shù)傳遞是一個重要的考量,傳統(tǒng)方法中,即使參數(shù)值為空,也會被包含在請求中發(fā)送給后端,這可能會導(dǎo)致不必要的數(shù)據(jù)處理,而優(yōu)化后的方法則只會傳遞那些實(shí)際有值的字段,從而提高數(shù)據(jù)傳輸?shù)挠行院秃蠖颂幚淼男?/div> 2024-10-10vue?項(xiàng)目優(yōu)雅的對url參數(shù)加密詳解
這篇文章主要為大家介紹了vue?項(xiàng)目優(yōu)雅的對url參數(shù)加密詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10vue2導(dǎo)入使用vue-codemirror組件的教程詳解
vue-codemirror是一個基于Vue的代碼在線編輯器組件,它封裝了CodeMirror編輯器,使得在Vue項(xiàng)目中可以方便地使用CodeMirror,下面我們就來看看vue-codemirror的具體使用吧2024-02-02最新評論