如何追蹤變化
1. Object.defineProperty 把屬性轉為 getter/setter,用以檢測數據的變化。
- 當把一個普通的 JavaScript 對象傳入 Vue 實例作為 data 選項,Vue 將遍歷此對象所有的 property,并使用 Object.defineProperty 把這些 property全部轉為 getter/setter。
- get 是一個函數,當屬性被訪問時,會觸發(fā) get 函數
- set 也是一個函數,當屬性被賦值時,會觸發(fā) set 函數,例如
var obj={
name:"響應式"
}
Object.defineProperty(obj,"name",{
get(){
console.log("get 被觸發(fā)")
},
set(val){
console.log("set 被觸發(fā)")
}
})
- 當訪問 obj.name 時,會打印 ' get 被觸發(fā) '
- 當給 obj.name 賦值時,obj.name = 'xxx',會打印 ' set 被觸發(fā) '
2. watcher ,把組件渲染接觸過的數據屬性記為依賴
- 每個組件實例都對應一個 watcher 實例,它會在組件渲染的過程中把“接觸”過的數據 property 記錄為依賴。之后當依賴項的 setter 觸發(fā)時,會通知 watcher,從而使它關聯(lián)的組件重新渲染。
由于 JavaScript 的限制,Vue 不能檢測數組和對象的變化,解決方法是手動調用 Vue.set 或者 this.$set
1. 檢測對象的變化
- 對于已經創(chuàng)建的實例,Vue 不允許動態(tài)添加根級別的響應式 property。
var vm = new Vue({
data:{
a:1
}
})
// `vm.a` 是響應式的
vm.b = 2
// `vm.b` 是非響應式的
- 使用 Vue.set或者vm.$set (object, propertyName, value) 方法向嵌套對象添加響應式屬性
Vue.set(vm.someObject, 'b', 2) //Vue.set
this.$set(this.someObject,'b',2) //vm.$set 實例方法,這也是全局 Vue.set 方法的別名
- 有時可能需要為已有對象賦值多個新 property,比如使用 Object.assign() 或 _.extend()。但是,這樣添加到對象上的新 property 不會觸發(fā)更新。在這種情況下,你應該用原對象與要混合進去的對象的 property 一起創(chuàng)建一個新的對象。
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
2. 檢測數組的變化
- 當你利用索引直接設置一個數組項和修改數組的長度時,Vue 不能檢測到數組的變動:
- 例如:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是響應性的
vm.items.length = 2 // 不是響應性的
- 利用索引直接設置一個數組項可以用對象的方式(Vue.set或者vm.$set (object, propertyName, value) ),也可以用splice方法
Vue.set(vm.items, indexOfItem, newValue) //Vue.set
vm.$set(vm.items, indexOfItem, newValue) //vm.$set
vm.items.splice(indexOfItem, 1, newValue) //splice
- 修改數組的長度也可以用splice,splice在只有一個參數的時候表示保留的數組的長度
vm.items.splice(newLength)
異步更新隊列
- Vue 在更新 DOM 時是異步執(zhí)行的
- 只要偵聽到數據變化,Vue 將開啟一個隊列,并緩沖在同一事件循環(huán)中發(fā)生的所有數據變更。如果同一個 watcher 被多次觸發(fā),只會被推入到隊列中一次(會在緩沖時去除重復數據)
- 所以如果想要在數據更新之后拿到數據,最好使用
Vue.nextTick(callback)或vm.$nextTick()實例方法