問題來源:https://segmentfault.com/q/10...vue
問題描述:Vue檢測數據的變更是經過Object.defineProperty實現的,因此沒法監聽數組的添加操做是能夠理解的,由於是在構造函數中就已經爲全部屬性作了這個檢測綁定操做。git
可是官方的原文:因爲 JavaScript 的限制, Vue 不能檢測如下變更的數組:github
當你利用索引直接設置一個項時,例如: vm.items[indexOfItem] = newValue
當你修改數組的長度時,例如: vm.items.length = newLength
這句話是什麼意思?我測試了下Object.defineProperty是能夠經過索引屬性來設置屬性的訪問器屬性的,那爲什麼作不了監聽?segmentfault
有些論壇上的人說由於數組長度是可變的,即便長度爲5,可是未必有索引4,我就想問問這個答案哪裏來的,修改length,新增的元素會被添加到最後,它的值爲undefined,經過索引同樣能夠獲取他們的值,怎麼就叫作「未必有索引4」了呢?數組
既然知道數組的長度爲什麼不能遍歷全部元素並經過索引這個屬性所有添加set和get不就能夠同時更新視圖了嗎?函數
若是非要說的話,考慮到性能的問題,假設元素內容只有4個有意義的值,可是長度確實1000,咱們不可能爲1000個元素作檢測操做。可是官方說的因爲JS限制,我想知道這個限制是什麼內容?各位大大幫我解決下這個問題,感謝萬分性能
面對這個問題,我想說的是,首先,長度爲1000,但只有4個元素的數組並不必定會影響性能,由於js中對數據的遍歷除了for循環還有forEach、map、filter、some等,除了for循環外(for,for...of),其餘的遍歷都是對鍵值的遍歷,也就是除了那四個元素外的空位並不會進行遍歷(執行回調),因此也就不會形成性能損耗,由於循環體中沒有操做的話,所帶來的性能影響能夠忽略不計,下面是長度爲10000,但只有兩個元素的數組分別使用for及forEach遍歷的結果:測試
var arr = [1]; arr[10000] = 1 function a(){ console.time() for(var i = 0;i<arr.length;i++)console.log(1) console.timeEnd() } a(); //default: 567.1669921875ms a(); //default: 566.2451171875ms function b(){ console.time() arr.forEach(item=>{console.log(2)}) console.timeEnd() } b(); //default: 0.81982421875ms b(); //default: 0.434814453125ms
能夠看到結果很是明顯,不過,若是for循環中不作操做的話二者速度差很少spa
其次,我要說的是,我也不知道這個限制是什麼 (⇀‸↼‶) ╮( •́ω•̀ )╭3d
Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。數組的索引也是屬性,因此咱們是能夠監聽到數組元素的變化的
var arr = [1,2,3,4] arr.forEach((item,index)=>{ Object.defineProperty(arr,index,{ set:function(val){ console.log('set') item = val }, get:function(val){ console.log('get') return item } }) }) arr[1]; // get 2 arr[1] = 1; // set 1
可是咱們新增一個元素,就不會觸發監聽事件,由於這個新屬性咱們並無監聽,刪除一個屬性也是。
再回到題主的問題,既然數組是能夠被監聽的,那爲何vue不能檢測vm.items[indexOfItem] = newValue
致使的數組元素改變呢,哪怕這個下標所對應的元素是存在的,且被監聽了的?
爲了搞清楚這個問題,我用vue的源碼測試了下,下面是vue對數據監測的源碼:
能夠看到,當數據是數組時,會中止對數據屬性的監測,咱們修改一下源碼:
使數據爲數組時,依然監測其屬性,而後在defineReactive函數中的get,set打印一些東西,方便咱們知道調用了get以及set。這裏加了個簡單判斷,只看數組元素的get,set
而後寫了一個簡單案例,主要測試使用vm.items[indexOfItem] = newValue
改變數組元素能不能被監測到,並響應式的渲染頁面
運行頁面
能夠看到,運行了6次get,咱們數組長度爲3,也就是說數組被遍歷了兩遍。兩遍很少,頁面渲染一次,可能屢次觸發一個數據的監聽事件,哪怕這個數據只用了一次,具體的須要看尤大代碼怎麼寫的。就拿這個來講,當監聽的數據爲數組時,會運行dependArray函數(代碼在上面圖中get的實現裏),這個函數裏對數組進行了遍歷取值操做,因此會多3遍get,這裏主要是vue對data中arr數組的監聽觸發了dependArray函數。
當咱們點擊其中一個元素的時候,好比我點擊的是3
能夠看到會先運行一次set,而後數據更新,從新渲染頁面,數組又是被遍歷了兩遍。
可是!!!數組確實變成響應式的了,也就是說js語法功能並不會限制數組的監測。
這裏咱們是用長度爲3的數組測試的,當我把數組長度增長到9時
能夠看到,運行了18次get,數組仍是被遍歷了兩遍,點擊某個元素同理,渲染的時候也是被遍歷兩次。
有了上面的實驗,個人結論是數組在vue中是能夠實現響應式更新的,可是不明白尤大是出於什麼考慮,沒有加入這一功能,但願有知道的大佬們不吝賜教
github上提問了尤大