問題描述:Vue偵測資料的變動是透過Object.defineProperty實現的,所以無法監聽陣列的新增操作是可以理解的,因為是在建構函式中就已經為所有屬性做了這個偵測綁定運算。
但官方的原文:由於JavaScript 的限制, Vue 無法偵測以下變動的陣列:
當你利用索引直接設定一個項目時,例如: vm.items[indexOfItem] = newValue
當你修改陣列的長度時,例如: vm.items.length = newLength
這句話是什麼意思?我測試了下Object.defineProperty是可以透過索引屬性來設定屬性的存取器屬性的,那為何做不了監聽?
有些論壇上的人說因為數組長度是可變的,即使長度為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迴圈中不做操作的話兩者速度差不多
其次,我要說的是,我也不知道這個限制是什麼 (⇀‸↼‶) ⠮( •́ω•̀ )╭
一個物件上定義一個新屬性,或修改一個物件的現有屬性, 並傳回這個物件。陣列的索引也是屬性,所以我們是可以監聽到陣列元素的變化的
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中是可以實現響應式更新的,但是不明白尤大是出於什麼考慮,沒有加入這一功能,希望有知道的大佬們不吝賜教
相關文章:
相關影片:
以上是Vue為什麼不能偵測陣列變動?原因如這些的詳細內容。更多資訊請關注PHP中文網其他相關文章!