Vue中數組更新後視圖不動態更新原因剖析

在Vue的官方文檔有提到這樣一個注意事項:

由於 JavaScript 的限制,Vue 不能檢測以下變動的數組:
1)當你利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue
2)當你修改數組的長度時,例如:vm.items.length = newLength

 也就是說,直接設置數組的某一項的值,雖然改變了數組的值,但視圖上顯示的仍爲數組之前的值,數據的響應失效了。出現這種現象的根本原因是什麼呢?

首先我們先來了解vue數據響應的原理。官方文檔的解釋:

當你把一個普通的 JavaScript 對象傳給 Vue 實例的 data 選項,Vue 將遍歷此對象所有的屬性,並使用 Object.defineProperty 把這些屬性全部轉爲 getter/setter。

也就是說當改變data中屬性的值時會觸發其相應setter的調用,從而實現響應的操作。但getter和setter是有侷限性的。我們先來看下面的這個例子:

var person = {};
Object.defineProperties( person, { 
      age: { 
        defaultValue: 11, 
        get: function () {
          return this.defaultValue;
        }, 
        set: function (val) { 
          this.defaultValue = val; 
          console.log("觸發了set") 
        }
      } 
    });

// 修改屬性的值時能夠觸發set
person.age = 12    // 觸發了set
->觸發了set
->12

person.age
->12

// 將屬性的值設置爲一個數組,當通過索引值修改數組的某一項或使用數組的某些方法修改數組時不能觸發set
person.age = [2,3,4]    // 觸發了set
->觸發了set
->(3) [2, 3, 4]

person.age[2] = 5      // 未觸發set
->5

person.age
->(3) [2, 3, 5]

person.age.push(5)     // 未觸發set
->4

person.age
->(4) [2, 3, 4, 5]


// 將屬性的值設置爲一個對象,當修改對象中某屬性的值時無法觸發set
person.age = { first: 1 }
->觸發了set
->{first: 1}

person.age.first = 2      // 未觸發set
->2

通過上述例子可以觀察得出:

當該屬性的值爲一個數組時,通過索引修改數組某一項的值或使用數組的某些方法修改數組並不能觸發set;當屬性的值爲一對象時,直接修改對象中屬性的值時也無法觸發set。

有問題就相應的有解決方案。官方文檔給出的解決方案如下:

爲了解決第一類問題,以下兩種方式都可以實現和 vm.items[indexOfItem] = newValue 相同的效果,同時也將觸發狀態更新:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)

// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)


你也可以使用 vm.$set 實例方法,該方法是全局方法 Vue.set 的一個別名:

vm.$set(vm.items, indexOfItem, newValue)

 

爲了解決第二類問題,你可以使用 splice

vm.items.splice(newLength)

而對於對象,當修改對象的屬性或爲對象添加屬性時應該使用以下方法:

Vue.set(vm.userProfile, 'age', 27)

vm.$set(vm.userProfile, 'age', 27)

有時你可能需要爲已有對象賦予多個新屬性,比如使用 Object.assign() 或 _.extend()。在這種情況下,你應該用兩個對象的屬性創建一個新的對象。所以,如果你想添加新的響應式屬性,則使用後一種方法來替代前一種方法:

// 不要使用此方法
Object.assign(vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

// 應該使用此方法
vm.userProfile = Object.assign({}, vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

額外的:由於數據響應原理機制, Vue 不允許動態添加根級響應式屬性,所以你必須在初始化實例前聲明所有可能用到的根級響應式屬性,且爲這些屬性都設一個初值,哪怕只是一個空值。

 

相關官方文檔的說明:

數組更新檢測注意事項

對象更改檢測注意事項深入

響應式原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章