我們知道通過object.defineproperty()劫持陣列為其設定getter和setter後,呼叫的陣列的push、splice、pop等方法改變陣列元素時並不會觸發陣列的setter,這就會造成使用上述方法改變陣列後,頁面上並不能及時體現這些變化,也就是陣列資料變化不是響應式的(對上述不了解的可以參考這篇文章)。但實際用vue開發時,對於響應式陣列,使用push、splice、pop等方法改變陣列時,頁面會及時體現這種變化,那麼vue中是如何實現的呢?
通過vue原始碼可以看出,vue重寫了陣列的push、splice、pop等方法。
1從上面可以看出array.js中重寫了陣列的push、pop、shift、unshift、splice、sort、reverse七種方法,重寫方法在實現時除了將陣列方法名對應的原始方法呼叫一遍並將執行結果返回外,還通過執行ob.dep.notify()將當前陣列的變更通知給其訂閱者,這樣當使用重寫後方法改變陣列後,陣列訂閱者會將這邊變化更新到頁面中。//src/core/observer/array.js23
//獲取陣列的原型array.prototype,上面有我們常用的陣列方法
4 const arrayproto =array.prototype5//
建立乙個空物件arraymethods,並將arraymethods的原型指向array.prototype
6 export const arraymethods =object.create(arrayproto)78
//列出需要重寫的陣列方法名
9 const methodstopatch =[
10 'push',
11 'pop',
12 'shift',
13 'unshift',
14 'splice',
15 'sort',
16 'reverse'17]
18//
遍歷上述陣列方法名,依次將上述重寫後的陣列方法新增到arraymethods物件上
19 methodstopatch.foreach(function
(method) 就是重寫後的方法
23 def(arraymethods, method, function
mutator (...args)
38if
(inserted) ob.observearray(inserted)
39//
將當前陣列的變更通知給其訂閱者
40ob.dep.notify()
41//
最後返回執行結果result
42return
result
43})
44 })
重寫完陣列的上述7種方法外,我們還需要將這些重寫的方法應用到陣列上,因此在observer建構函式中,可以看到在監聽資料時會判斷資料型別是否為陣列。當為陣列時,如果瀏覽器支援__proto__,則直接將當前資料的原型__proto__指向重寫後的陣列方法物件arraymethods,如果瀏覽器不支援__proto__,則直接將arraymethods上重寫的方法直接定義到當前資料物件上;當資料型別為非陣列時,繼續遞迴執行資料的監聽。
1經過上述處理後,對於陣列,當我們呼叫其方法處理陣列時會按照如下原型鏈來獲取陣列方法://src/core/observer/index.js
2export class observer else
15this
.observearray(value)
16 } else19}
20...21}
22function
protoaugment (target, src: object)
27function copyaugment (target: object, src: object, keys: array)
32 }
對於響應式陣列,當瀏覽器支援__proto__屬性時,使用push等方法時先從其原型arraymethods上尋找push方法,也就是重寫後的方法,處理之後陣列的變化會通知到其訂閱者,更新頁面,當在arraymethods上查詢不到時會向上在array.prototype上查詢;當瀏覽器不支援__proto__屬性時,使用push等方法時會先從陣列自身上查詢,如果查詢不到會向上再array.prototype上查詢。
對於非響應式陣列,當使用push等方法時會直接從array.prototype上查詢。
值得一提的是原始碼中通過判斷瀏覽器是否支援__proto__來分別使用protoaugment和copyaugment 方法將重寫後的陣列方法應用到陣列中,這是因為對於ie10及以下的ie瀏覽器是不支援__proto__屬性的:
上述截圖參考於vue原始碼解析五——資料響應系統
結論:在將陣列處理成響應式資料後,如果使用陣列原始方法改變陣列時,陣列值會發生變化,但是並不會觸發陣列的setter來通知所有依賴該陣列的地方進行更新,為此,vue通過重寫陣列的某些方法來監聽陣列變化,重寫後的方法中會手動觸發通知該陣列的所有依賴進行更新。
vue如何監聽陣列變化?
vue.js觀察陣列變化主要通過以下7個方法 push pop shift unshift splice sort reverse 怎麼實現?通過對data資料中陣列的這7個方法進行重新包裝 注意只是data資料中的陣列 為什麼不直接對array.prototype的原型方法進行重新包裝?因為不應該...
vue中是怎樣監聽陣列變化的
我們知道通過object.defineproperty 劫持陣列為其設定getter和setter後,呼叫的陣列的push splice pop等方法改變陣列元素時並不會觸發陣列的setter,這就會造成使用上述方法改變陣列後,頁面上並不能及時體現這些變化,也就是陣列資料變化不是響應式的 對上述不了...
vue監聽陣列變化
1 觸發更新檢視 2function updateview 56 重新定義陣列原型 7 const oldarrayproperty array.prototype8 建立新物件,原型指向 oldarrayproperty 再擴充套件新的方法不會影響原型 9 const arrproto objec...