Vue在數組改變元素時不更新view問題--歡聚時代一面

因爲vue實現雙向數據綁定的機制是數據劫持,也就是在所有對象上有個Object.defineProperty()方法,通過監聽set,get方法去實現,而數組沒有這兩個方法,所以就不會更新view

答案在Vue的API手冊中https://cn.vuejs.org/v2/guide/list.html#%E6%95%B0%E7%BB%84%E6%9B%B4%E6%96%B0%E6%A3%80%E6%B5%8B

 

數組更新檢測

變異方法 (mutation method)

Vue 將被偵聽的數組的變異方法進行了包裹,所以它們也將會觸發視圖更新。這些被包裹過的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

你可以打開控制檯,然後對前面例子的 items 數組嘗試調用變異方法。比如 example1.items.push({ message: 'Baz' })

替換數組

變異方法,顧名思義,會改變調用了這些方法的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如 filter()concat() 和 slice() 。它們不會改變原始數組,而總是返回一個新數組。當使用非變異方法時,可以用新數組替換舊數組:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

你可能認爲這將導致 Vue 丟棄現有 DOM 並重新渲染整個列表。幸運的是,事實並非如此。Vue 爲了使得 DOM 元素得到最大範圍的重用而實現了一些智能的啓發式方法,所以用一個含有相同元素的數組去替換原來的數組是非常高效的操作。

注意事項

由於 JavaScript 的限制,Vue 不能檢測以下數組的變動:

  1. 當你利用索引直接設置一個數組項時,例如:vm.items[indexOfItem] = newValue
  2. 當你修改數組的長度時,例如:vm.items.length = newLength

舉個例子:

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是響應性的
vm.items.length = 2 // 不是響應性的

爲了解決第一類問題,以下兩種方式都可以實現和 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)

對象變更檢測注意事項

還是由於 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 現在是響應式的

vm.b = 2
// `vm.b` 不是響應式的

對於已經創建的實例,Vue 不允許動態添加根級別的響應式屬性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套對象添加響應式屬性。例如,對於:

var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})

你可以添加一個新的 age 屬性到嵌套的 userProfile 對象:

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

你還可以使用 vm.$set 實例方法,它只是全局 Vue.set 的別名:

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'
})

顯示過濾/排序後的結果

有時,我們想要顯示一個數組經過過濾或排序後的版本,而不實際改變或重置原始數據。在這種情況下,可以創建一個計算屬性,來返回過濾或排序後的數組。

例如:

<li v-for="n in evenNumbers">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

在計算屬性不適用的情況下 (例如,在嵌套 v-for 循環中) 你可以使用一個方法:

<li v-for="n in even(numbers)">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}
發佈了34 篇原創文章 · 獲贊 6 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章