🔥🔥Vue2.0爲何不能檢查數組的變化?又該如何解決?


image.png

這是我參與更文挑戰的第7天,活動詳情查看: 更文挑戰html


前言

咱們都知道,Vue2.0對於響應式數據的實現有一些不足:vue

  • 沒法檢測數組/對象的新增
  • 沒法檢測經過索引改變數組的操做。

分析

  • 沒法檢測數組/對象的新增?git

    Vue檢測數據的變更是經過Object.defineProperty實現的,因此沒法監聽數組的添加操做是能夠理解的,由於是在構造函數中就已經爲全部屬性作了這個檢測綁定操做。github

  • 沒法檢測經過索引改變數組的操做。即vm.items[indexOfItem] = newValue?web

    官方文檔中對於這兩點都是簡要的歸納爲「因爲JavaScript的限制」沒法實現,而Object.defineProperty是實現檢測數據改變的方案,那這個限制是指Object.defineProperty嗎?數組

思考

vm.items[indexOfItem] = newValue真的不能被監聽麼?

Vue對數組的7個變異方法(push、pop、shift、unshift、splice、sort、reverse)實現了響應式。這裏就不作測試了。咱們測試一下經過索引改變數組的操做,能不能被監聽到。markdown

遍歷數組,用Object.defineProperty對每一項進行監測ide

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)
複製代碼

image-20210607020953993

測試說明函數

經過索引改變arr[1],咱們發現觸發了set,也就是Object.defineProperty是能夠檢測到經過索引改變數組的操做的,那Vue2.0爲何沒有實現呢?是尤大能力不行?這確定毋庸置疑。那他爲何不實現呢?oop

後續

後來我在網上找的時候發現了這個。🌀🔥這是github上,一位開發小哥對尤大提到問題。

image-20210607021557820


小結:原來是出於對性能緣由的考慮,沒有去實現它。而不是不能實現。

對於對象而言,每一次的數據變動都會對對象的屬性進行一次枚舉,通常對象自己的屬性數量有限,因此對於遍歷枚舉等方式產生的性能損耗能夠忽略不計,可是對於數組而言呢?數組包含的元素量是可能達到成千上萬,假設對於每一次數組元素的更新都觸發了枚舉/遍歷,其帶來的性能損耗將與得到的用戶體驗不成正比,故vue沒法檢測數組的變更。

不過Vue3.0用proxy代替了defineProperty以後就解決了這個問題。


解決方案

數組
  1. ​ this.$set(array, index, data)

    //這是個深度的修改,某些狀況下可能致使你不但願的結果,所以最好仍是慎用
    this.dataArr = this.originArr
    this.$set(this.dataArr, 0, {data: '修改第一個元素'})
    console.log(this.dataArr)        
    console.log(this.originArr)  //一樣的 源數組也會被修改 在某些狀況下會致使你不但願的結果 
    複製代碼
  2. splice

    //由於splice會被監聽有響應式,而splice又能夠作到增刪改。
    複製代碼
  3. 利用臨時變量進行中轉

    let tempArr = [...this.targetArr]
    tempArr[0] = {data: 'test'}
    this.targetArr = tempArr
    複製代碼
對象
  1. this.$set(obj, key ,value) - 可實現增、改

  2. watch時添加deep:true深度監聽,只能監聽到屬性值的變化,新增、刪除屬性沒法監聽

    this.$watch('blog', this.getCatalog, {
        deep: true
        // immediate: true // 是否第一次觸發
      });
    複製代碼
  3. watch時直接監聽某個key

    watch: {
      'obj.name'(curVal, oldVal) {
        // TODO
      }
    }
    複製代碼

🔥最後:你們有什麼不一樣的看法,歡迎留言討論。要是以爲不錯,給小攻城獅點個讚唄👀

相關文章
相關標籤/搜索