前言
Vue2.0對於響應式數據的實現有一些不足:
- 無法檢測數組/對象的新增
- 無法檢測通過索引改變數組的操作。
Vue2.0中響應式數據是通過Object.defineProperty實現,因此無法檢測數組/對象的新增,但為什么無法檢測到通過索引改變數組的操作呢?也是因為Object.defineProperty的原因么?
官方文檔中對於這兩點都是簡要的概括為“由於JavaScript的限制”無法實現,而Object.defineProperty是實現檢測數據改變的方案,那這個限制是指Object.defineProperty嗎?
無法檢測數組的索引變化?
我們來測試一下看看。以下例子,對遍歷數組中的每一項,用Object.defineProperty對其進行監測
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function defineGet() {
console.log(`get key: ${key} value: ${value}`)
return value
},
set: function defineSet(newVal) {
console.log(`set key: ${key} value: ${newVal}`)
value = newVal
}
})
}
function observe(data) {
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key])
})
}
let arr = [1, 2, 3]
observe(arr)
說明:Vue對數組的7個變異方法(push、pop、shift、unshift、splice、sort、reverse)實現了響應式,因此這里我們就不做測試了。
通過索引改變arr[1],可以發現觸發了set,也就是Object.defineProperty是可以檢測到通過索引改變數組的操作的,那Vue2.0為什么沒有實現呢?
一位開發小哥在github對尤大大提了issue,回答如下:
尤大大回答是因為性能問題。看來不是JavaScript的鍋,更不是Object.defineProperty的鍋了。
驗證
通過我們上面的測試,Object.property是可以檢測到通過索引改變數組的操作的,而Vue沒有實現。那我們看看源碼,Vue的實現邏輯是怎樣的?
我們改下對數組的操作邏輯,遍歷數組對每一項采用Object.defineProperty進行檢測
此時點擊按鈕2,可以發現數值改變為0了,說明通過索引改變arr的操作被檢測到了,Vue是可以實現的!我們進一步確認下:
在defineReactive的Object.defineProperty的set中,加一行console.log,可以發現,當點擊按鈕2時,控制台會輸出“set 0”,更加確切的說明arr[index]=0
被set檢測到了。
小結
由於以上提到的問題,Vue3.0采用Proxy替代了Object.defineProperty,Proxy可以完美解決嗎?下篇文章研究研究~
參考:
為什么Vue3.0使用Proxy實現數據監聽(defineProperty表示不背這個鍋)
記一次思否問答的問題思考:Vue為什么不能檢測數組變動