用 Vue 來觀察屬性變化
響應系統是Vue 一個顯著功能,修改屬性,可以更新視圖,這讓狀態管理變得非常簡單且直觀。
創建 Vue 實例時,Vue 將遍歷 data 的屬性,通過 ES5 的 Object.defineProperty 將它們轉為 getter/setter,在其內部 Vue 可以追蹤依賴、通知變化。
const vm = new Vue({
data: {foo: 1} // 'vm.foo' (在內部,同 'this.foo') 是響應的
})
觀察屬性變化
Vue 的實例提供了 $watch 方法,用于觀察屬性變化。
const vm = new Vue({
data: {foo: 1}
})
vm.$watch('foo', function (newValue, oldValue) {
console.log(newValue, oldValue) // 輸出 2 1
console.log(this.foo) // 輸出 2
})
vm.foo = 2
當屬性變化后,響應函數將會被調用,在其內部, this 自動綁定到 Vue 的實例 vm 上。
需要注意的是,響應是異步的。如下:
const vm = new Vue({
data: {foo: 1}
})
vm.$watch('foo', function (newValue, oldValue) {
console.log('inner:', newValue) // 后輸出 "inner" 2
})
vm.foo = 2
console.log('outer:', vm.foo) // 先輸出 "outer" 2
通過 $watch Vue 實現了數據和視圖的綁定。觀察到數據變化,Vue 便 異步 更新 DOM ,在同一事件循環內,多次數據變化將會被緩存起來,在下次事件循環中,Vue 刷新隊列并僅執行必要的更新。如下:
const vm = new Vue({
data: {foo: 1}
})
vm.$watch('foo', function (newValue, oldValue) {
console.log('inner:', newValue) // 后只輸出一次 "inner" 5
})
vm.foo = 2
vm.foo = 3
vm.foo = 4
console.log('outer:', vm.foo) // 先輸出 "outer" 4
vm.foo = 5
計算屬性
MV* 中,將 Model 層數據展現到 View,經常有復雜的數據處理邏輯,這種情況下,使用計算屬性 (computed property) 更加明智。
const vm = new Vue({
data: {
width: 0,
height: 0,
},
computed: {
area () {
let output = ''
if (this.width > 0 && this.height > 0) {
const area = this.width * this.height
output = area.toFixed(2) + 'm2'
}
return output
}
}
})
vm.width = 2.34
vm.height = 5.67
console.log(vm.area) // 輸出 "13.27m2"
在計算屬性內部, this 自動綁定 vm ,因此 聲明計算屬性時需要避免使用箭頭函數 。
上例中, vm.width 和 vm.height 是響應的, vm.area 內部首次讀取 this.width 和 this.height 時,Vue 收集其做為 vm.area 的依賴,此后 vm.width 或 vm.height 變化時, vm.area 重新求值。 計算屬性是基于它的依賴緩存 ,如果 vm.width 和 vm.height 沒有變化,多次讀取 vm.area ,會立即返回之前的計算結果,而不必再次求值。
同樣由于 vm.width 和 vm.height 是響應的,在 vm.area 中可以 將依賴的屬性賦值給一個變量,通過讀取變量來減少讀取屬性次數,同時解決在條件分支中,Vue 有時會無法收集到依賴的問題 。
實現如下:
const vm = new Vue({
data: {
width: 0,
height: 0,
},
computed: {
area () {
let output = ''
const {width, height} = this
if (width > 0 && height > 0) {
const area = width * height
output = area.toFixed(2) + 'm2'
}
return output
}
}
})
vm.width = 2.34
vm.height = 5.67
console.log(vm.area) // 輸出 "13.27m2"
通過 ob.js 單獨使用 Vue 的屬性觀察模塊
為方便學習和使用,ob.js 將 Vue 中屬性觀察模塊提取并封裝了一下。
安裝
npm install --save ob.js
觀察屬性變化
const target = {a: 1}
ob(target, 'a', function (newValue, oldValue) {
console.log(newValue, oldValue) // 3 1
})
target.a = 3
添加計算屬性
const target = {a: 1}
ob.compute(target, 'b', function () {
return this.a * 2
})
target.a = 10
console.log(target.b) // 20
像聲明 Vue 實例一樣傳入參數集合
const options = {
data: {
PI: Math.PI,
radius: 1,
},
computed: {
'area': function () {
return this.PI * this.square(this.radius)
},
},
watchers: {
'area': function (newValue, oldValue) {
console.log(newValue) // 28.274333882308138
},
},
methods: {
square (num) {
return num * num
},
},
}
const target = ob.react(options)
target.radius = 3
來自:https://segmentfault.com/a/1190000007494308