computed 有 getter
(取值) 和 setter
(寫入值)可使用,但預設只會有 getter
使用,因此 computed 預設是唯讀,不能寫入值。每次建立元件時,都會先跑一次 computed 裏的 getter 來取得此 computed 函式裏的值。之後如果該 computed 函式裏的依賴沒有變化,就不會再跑此 computed 函式,即是不會跑 此函式的getter
,並只會一直回傳之前緩存起來的值。
另外,如果要使用 setter,我們可以透過在 computed 屬性裏建立物件的方法來建立 getter
和 setter
。
以下會再作詳細解說。
預設 computed 只有 getter
,意思是我只能透過 computed 函式 來取值。
例如我嘗試在 mounted,觸發 total
函式裏的 setter
:
<div id="app">
<p> {{ total }} </p>
</div>
data() {
return {
price: 100,
discount: 0.8
}
},
computed: {
total() {
return this.price * this.discount
}
},
mounted() {
this.total = 1000
}
報錯:
[Vue warn]: Write operation failed: computed property 'total' is readonly.
Vue 官網有提到,雖然預設只有 getter 可使用,但我們可以使用物件,並在裏面使用 getter 和 setter。
當你打開 console 查看會看到其實 computed 屬性(以 total 為例)也會有 set
:
以下例子示範如何使用預設沒有的 setter
。注意:需要使用物件來建立 getter
和 setter
:
<div id="app">
<p> 定價: {{ price }} </p>
<p>折扣:{{ discount }} </p>
<p>折扣價:{{ total }}</p>
<button @click="changeComputed">按此隨機產生折扣</button>
</div>
data() {
return {
price: 100,
discount: 0.5
}
},
computed: {
// 需使用物件
total: {
get() {
console.log('觸發 getter!')
return this.price * this.discount
},
set(newVal) {
console.log('觸發 setter!')
this.discount = newVal
}
}
},
methods: {
changeComputed() {
// 觸發 set,再觸發 get
this.total = Number(Math.random().toFixed(1))
}
}
結果:
程式碼:
https://codepen.io/alysachan/pen/QWgojov
this.total = Number(Math.random().toFixed(1))
這行雖然看來好像怪怪的?看似是直接把 total
改為隨機產生出來的 discount。但事實上,這裏所做的事是觸發 set
函式,並非修改 total
的值。
重溫一下原生 JavaScript 裏 set
(setter) 的用法:
const obj = {
num: 0,
get add(){
return this.num
},
set add(newVal){
this.num += newVal
}
}
// 使用 get
console.log(obj.add) // 0
// 使用 set
console.log(obj.add = 100) //100
以上 obj.add = 100
,就是把 100 帶進去 add
函式裏,並非改掉 add 函式所回傳的值。
因此,回到 Vue 的例子:this.total = Number(Math.random().toFixed(1))
。就是把右邊的值,帶進去 total
的 set
函式裏,也即是 newVal
參數的值。並且修改了 discount
的值。
但為什麼畫面明明顯示total
的值被修改了?因為在 computed 函式裏,所有用到的 data 資料,都會是該函式的依賴。因此當 this.discount
有變化時,就會觸發 total
的更新。
所以整個運作次序就是:
set
,修改 discount
打開 console 會看到,先觸發 setter,之後才是 getter:
注意,觸發了 set 不代表一定會觸發 get。如果在 set 裏沒有修改到 get 裏的任何一個依賴,get 就不會被觸發,即是不會更新在畫面中 total 的值。例如我只是在 set 裏印出參數:
total: {
get() {
return this.price * this.discount
},
set(newVal) {
console.log(newVal)
}
}
如果畫面中沒有用到 total
,只剩下 price
和 discount
:
<div id="app">
<p> 定價: {{ price }} </p>
<p>折扣:{{ discount }} </p>
<!-- <p>折扣價:{{ total }}</p> -->
<button @click="changeComputed">按此隨機產生折扣</button>
</div>
這情況裏,即使按按鈕,修改了 this.discount
,也不會觸發 total
裏的 get
。反之,只會一直印出 觸發 setter!
文字。除非你打開了 Vue devtool 檢查工具看一次,之後 Vue 才會執行 getter,並印出 觸發 getter!
文字。
因此可以總結一句,如果在畫面沒用到此 computed 函式,而且沒有在其他程式碼裏有使用過此 computed 函式的值。即代表不會跑該 computed 函式裏的 get,也就是沒有更新此值。
不過這情況不用怎樣擔心,即是如果在畫面上沒用過此值,但你在其他程式碼用到時,還是會寫 this.total
。一旦寫了 this.total
,就會觸發 total
的 getter,也就一定會取得最新的值。
當然,另一個情況就是當依賴的資料沒變化,就不會執行 getter,並且會一直回傳之前緩存起來的值,也就是 computed 的緩存特性。
例如改一下之前的例子,按按鈕後,把 discount 改為 0.5。結果只會一直印出「觸發 setter!」,因為資料裏的 discount 也是 0.5,結果就沒變化:
methods: {
changeComputed() {
// 只會觸發 set
// 一直都是改為 0.5
this.total = 0.5
}
},
computed
預設只使用 getter
,沒有 setter
。因此 computed
函式只會是唯讀。computed
裏加入 setter
,但注意要以物件型別來寫,並在裏面加入 getter
和 setter
。computed
函式的值,而在程式碼中也沒有用過此值,Vue 就不會跑該函式裏的 getter
,也不知道會回傳什麼值。那些關於 Vue 的小細節 - Computed 中 getter 和 setter 觸發的時間點
Vue 筆記 - Computed 的 get() 與 set()
属性的 getter 和 setter