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