原理
<div id="app">
{{num}}
{{num1}}
</div>
var app = new Vue({
el: '#app',
data: {
num: 1
},
computed: {
num1 () {
return this.num + 1;
}
}
});
console.log(app);
computed 本質是一個惰性求值的訂閱者。data 屬性的 Observer 掛在 _data
屬性下,而 computed 屬性掛在 _computedWatchers
下。而發布者 Dep 里存放了兩個訂閱者,而和computed相關的訂閱者,其實只做了一件事情,標記 dirty 為 true,等待 get 時再真正計算。
computed 內部實現了一個惰性的 watcher,也就是 _computedWatchers,_computedWatchers 不會立刻求值,同時持有一個 dep 實例。
其內部通過 this.dirty 屬性標記計算屬性是否需要重新求值
變更流程如下:
- 當 computed 的依賴的狀態發生改變時,就會通知這個惰性的 watcher。
- computed watcher 通過 this.dep.subs.length 判斷有沒有訂閱者,
- 有的話,會重新計算,然后對比新舊值,如果變化了,會重新渲染。 (Vue 想確保不僅僅是計算屬性依賴的值發生變化,而是當計算屬性最終計算的值發生變化時才會觸發渲染 watcher 重新渲染,本質上是一種優化。)
- 沒有的話,僅僅把
this.dirty = true
。 (當計算屬性依賴於其他數據時,屬性並不會立即重新計算,只有之后其他地方需要讀取屬性的時候,它才會真正計算,即具備 lazy(懶計算)特性。)
watch
這是 computed 的 watcher 觀察者結構。特有的 lazy 為 true 代表 getter 時需要計算更新,注意 cb 只是一個空的函數,啥也不做,但是 expression 有值
Watcher {
vm: Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
deep: false
user: false
lazy: true
sync: false
before: undefined
cb: function noop(a, b, c) {}
id: 1
active: true
dirty: false
deps: (2) [Dep, Dep]
newDeps: []
depIds: Set(2) {3, 6}
newDepIds: Set(0) {}
expression: "len() {↵ return this.message.length↵ }"
getter: ƒ len()
value: 2
__proto__: Object
}
這是 watch 的 觀察者結構,注意 user 為 true,expression 里沒有關鍵內容,但是 cb 里有,更新值的時候就是判定 user 為 true 時,調用 cb 獲取新值。
Watcher {
vm: Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
deep: false
user: true
lazy: false
sync: false
before: undefined
cb: function message() {
this.full = this.message.join(',');
}
id: 2
active: true
dirty: false
deps: (2) [Dep, Dep]
newDeps: []
depIds: Set(2) {3, 6}
newDepIds: Set(0) {}
expression: "message"
getter: ƒ (obj)
value: (2) ["a", "c", __ob__: Observer],
__proto__: Object
}
與 watch 有什么區別
computed 計算屬性 : 依賴其它屬性值,並且 computed 的值有緩存,只有它依賴的屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值。
watch 偵聽器 : 更多的是「觀察」的作用,無緩存性,類似於某些數據的監聽回調,每當監聽的數據變化時都會執行回調進行后續操作。
與 watch 運用場景的對比
需要在數據變化時執行異步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行異步操作 ( 訪問一個 API ),限制我們執行該操作的頻率,並在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。
當我們需要進行數值計算,並且依賴於其它數據時,應該使用 computed,因為可以利用 computed 的緩存特性,避免每次獲取值時,都要重新計算。