詳解Vue2如何監(jiān)聽數(shù)組的變化
前言
眾所周知,vue2的響應(yīng)式原理是 數(shù)據(jù)劫持結(jié)合發(fā)布訂閱模式.具體是通過Object.defineProperty()方法來劫持各個(gè)屬性的getter和setter,從而能夠監(jiān)聽到數(shù)據(jù)的變化。,但是Object.defineProperty不能監(jiān)聽數(shù)組的變化那么vue2是怎么實(shí)現(xiàn)數(shù)組響應(yīng)式的呢?而且在日常開發(fā)中,我們會(huì)發(fā)現(xiàn)不能直接修改數(shù)組的length長(zhǎng)度,也不能通過數(shù)組下標(biāo)的方式修改數(shù)據(jù),比如:arr[0]=123這種方式不能響應(yīng)式。
那么,vue2是如何實(shí)現(xiàn)的呢?
Vue2內(nèi)部通過重寫數(shù)組的原型方法來監(jiān)聽數(shù)組的變動(dòng)
具體來說,Vue2首先獲取到數(shù)組的原型,然后創(chuàng)建一個(gè)新的對(duì)象繼承自該原型,接著將這個(gè)新對(duì)象的原型上的七個(gè)能夠修改數(shù)組自身的方法(push、pop、shift、unshift、splice、sort、reverse)進(jìn)行重寫。這些方法在執(zhí)行時(shí),除了執(zhí)行其原有的邏輯之外,還會(huì)觸發(fā)視圖更新。
以下是一個(gè)簡(jiǎn)化的重寫示例:
// 獲取數(shù)組的原型 const arrayProto = Array.prototype; // 創(chuàng)建一個(gè)新的對(duì)象,該對(duì)象的原型就是arrayProto const arrayMethods = Object.create(arrayProto); // 需要被改寫的方法 const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]; methodsToPatch.forEach(function(method) { // 緩存原始方法 const original = arrayProto[method]; // 定義新的方法 Object.defineProperty(arrayMethods, method, { value: function mutator(...args) { // 先執(zhí)行原始方法 const result = original.apply(this, args); // 獲取數(shù)組對(duì)象的__ob__屬性,__ob__是每個(gè)響應(yīng)式對(duì)象都有的一個(gè)屬性,指向該對(duì)象的Observer實(shí)例 const ob = this.__ob__; // 如果方法是新增元素的操作,將新增的元素轉(zhuǎn)換為響應(yīng)式 let inserted; switch (method) { case 'push': case 'unshift': inserted = args; break; case 'splice': inserted = args.slice(2); break; } if (inserted) ob.observeArray(inserted); // 通知變更 ob.dep.notify(); return result; }, configurable: true, enumerable: false, writable: true }); });
如何使用
在初始化響應(yīng)式數(shù)據(jù)時(shí),Vue會(huì)判斷一個(gè)對(duì)象是否是數(shù)組。如果是數(shù)組,Vue則會(huì)將這個(gè)數(shù)組的原型指向上面提到的arrayMethods,從而使得這個(gè)數(shù)組調(diào)用7個(gè)修改自身的方法時(shí),能夠觸發(fā)視圖的更新。這一過程主要是在Observer類的實(shí)例化過程中完成的。
if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods); } else { copyAugment(value, arrayMethods, arrayKeys); } this.observeArray(value); }
小結(jié)
通過這種方式,Vue 2可以監(jiān)測(cè)到數(shù)組的變化并作出響應(yīng)。這種方法雖然巧妙,但有其局限性,比如直接通過索引設(shè)置數(shù)組元素的值或修改數(shù)組長(zhǎng)度等操作,是無法被檢測(cè)到的。Vue 3中采用了Proxy代替了這種實(shí)現(xiàn)方式,能夠更好地解決這些問題。
解決方式
雖然Object.defineProperty本身無法攔截?cái)?shù)組索引的直接修改或數(shù)組長(zhǎng)度的變化,Vue 2提供了幾種方法來解決這個(gè)限制,確保開發(fā)者仍然可以以響應(yīng)式的方式更新數(shù)組:
使用Vue.set 或 vm.$set
為了解決直接通過索引修改數(shù)組元素的問題,Vue 2引入了Vue.set函數(shù)和vm.$set實(shí)例方法。這兩個(gè)方法允許開發(fā)者在指定索引處插入或替換數(shù)組元素,同時(shí)保證變化是響應(yīng)式的。
// 假設(shè)有一個(gè)Vue組件的data如下: data() { return { fruits: ['apple', 'banana', 'cherry'] }; }, methods: { updateFruit() { this.$set(this.fruits, 1, 'orange'); // 將索引1處的'banana'替換為'orange' } }
使用數(shù)組的splice方法
另一個(gè)解決方案是使用數(shù)組的splice方法。splice不僅可以在數(shù)組中添加/刪除項(xiàng)目,而且由于Vue重寫了這個(gè)方法,使用它進(jìn)行的任何操作都會(huì)觸發(fā)視圖更新。
updateFruit() { this.fruits.splice(1, 1, 'orange'); // 同樣的效果,替換操作 }
響應(yīng)式系統(tǒng)的限制與規(guī)避策略
雖然Vue的響應(yīng)式系統(tǒng)提供了強(qiáng)大的數(shù)據(jù)綁定能力,但了解其內(nèi)部工作原理和限制對(duì)于開發(fā)高效、可維護(hù)的Vue應(yīng)用至關(guān)重要。通過正確地使用Vue提供的工具和方法(如Vue.set、vm.$set和splice),開發(fā)者可以確保即使是那些原生JavaScript限制下不可直接偵測(cè)的變化,也能被Vue的響應(yīng)式系統(tǒng)捕獲并正確地更新視圖。
面試題
Object.defineProperty如何監(jiān)聽數(shù)組?為什么無法獲取數(shù)組的變化?
Object.defineProperty 本身并不直接用于監(jiān)聽數(shù)組的變化,因?yàn)樗窃O(shè)計(jì)來劫持和監(jiān)聽對(duì)象屬性的讀取和寫入操作的。當(dāng)我們使用 Object.defineProperty 對(duì)對(duì)象的屬性進(jìn)行劫持時(shí),我們實(shí)際上是在設(shè)置屬性的 getter 和 setter,這樣每當(dāng)屬性被訪問或修改時(shí),我們就可以執(zhí)行自定義的邏輯,比如通知視圖進(jìn)行更新。然而,當(dāng)應(yīng)用到數(shù)組上時(shí),存在幾個(gè)核心限制使得 Object.defineProperty 無法有效地監(jiān)聽數(shù)組的變化:
1. 數(shù)組索引的修改
當(dāng)通過索引直接修改數(shù)組(如 arr[0] = 'new value')時(shí),這實(shí)際上是一個(gè)屬性賦值操作。雖然理論上可以對(duì)數(shù)組的每個(gè)索引使用 Object.defineProperty 來監(jiān)聽變化,但這在實(shí)踐中是不可行的,因?yàn)椋?/p>
性能問題:數(shù)組可能非常大,為每個(gè)索引設(shè)置 getter 和 setter 會(huì)極大地影響性能。
動(dòng)態(tài)性問題:數(shù)組長(zhǎng)度是動(dòng)態(tài)變化的,每次數(shù)組變化時(shí)都需要重新為新的索引設(shè)置劫持,這在技術(shù)上是復(fù)雜且低效的。
2. 修改數(shù)組長(zhǎng)度
直接修改數(shù)組的 length 屬性(例如,通過設(shè)置 arr.length = 0 來清空數(shù)組),這種操作同樣無法被 Object.defineProperty 直接偵測(cè)到。這是因?yàn)?length 屬性的變化不會(huì)觸發(fā)索引屬性的 setter。
3. 使用數(shù)組方法
數(shù)組的方法(如 push、pop、splice 等)可以修改數(shù)組的內(nèi)容或結(jié)構(gòu)。這些操作不僅改變數(shù)組元素,有時(shí)還會(huì)改變數(shù)組的長(zhǎng)度。Object.defineProperty 無法直接攔截這些方法調(diào)用,因?yàn)樗鼈兪菙?shù)組原型上的方法,而不是數(shù)組實(shí)例上的直接屬性。
Vue 2 如何實(shí)現(xiàn)數(shù)組的響應(yīng)式
正因?yàn)樯鲜鱿拗?,Vue 2 選擇了一種不同的方式來實(shí)現(xiàn)對(duì)數(shù)組的響應(yīng)式監(jiān)聽:
重寫數(shù)組方法:Vue 2 通過修改數(shù)組實(shí)例的原型,將數(shù)組的一些方法(如 push、pop 等)重寫為可以觸發(fā)視圖更新的版本。當(dāng)這些重寫的方法被調(diào)用時(shí),Vue 可以捕獲到數(shù)組的變動(dòng)并觸發(fā)相應(yīng)的更新。
總結(jié)來說,Object.defineProperty 由于其內(nèi)在的機(jī)制和限制,并不能直接用于有效監(jiān)聽數(shù)組的變化。Vue 2 通過一種巧妙的方式繞過了這些限制,能夠?qū)崿F(xiàn)對(duì)數(shù)組操作的響應(yīng)式更新。
到此這篇關(guān)于詳解Vue2如何監(jiān)聽數(shù)組的變化的文章就介紹到這了,更多相關(guān)Vue2監(jiān)聽數(shù)組變化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中的路由跳轉(zhuǎn)tabBar圖片和文字的高亮效果
這篇文章主要介紹了vue中的路由跳轉(zhuǎn)tabBar圖片和文字的高亮效果,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Monaco-editor 的 JSON Schema 配置及使用介紹
這篇文章主要為大家介紹了Monaco-editor 的 JSON Schema 配置及使用介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10vue2.0基于vue-cli+element-ui制作樹形treeTable
這篇文章主要介紹了vue2.0基于vue-cli+element-ui制作樹形treeTable,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Vite打包項(xiàng)目后圖片丟失的簡(jiǎn)單解決方法
vue項(xiàng)目完成打包上線的時(shí)候很多人都會(huì)碰到靜態(tài)資源找不到的情況,下面這篇文章主要給大家介紹了關(guān)于Vite打包項(xiàng)目后圖片丟失的簡(jiǎn)單解決方法,需要的朋友可以參考下2023-05-05