# Vue computed 運作原理:何謂綁定資料、getter、與methods和watch的分別
## Computed
- 有緩存的功能,只要該`computed`所綁定的值沒有改變,`computed`函式就不會執行
- 不可對`computed`寫入值,因為`computed`只有`getter`屬性,沒有`setter`
## 何謂`computed`所綁定的值?
官方說法:
> 计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。
官方例子:
https://cn.vuejs.org/v2/guide/computed.html
以下情況,我打算只按按鈕改變`quantity`,沒改變`price`,這樣會觸發computed函式嗎?
HTML:
```htmlembedded=
<div id="app">
<label for="price">輸入原價:</label>
<br>
<input type="number" v-model.number="price" id="price">
<p> 原價: {{ price }} </p>
<p> 折扣價: {{ discount }} </p>
<p> 購買數量: {{ quantity }}</p>
<button @click="addQuantity">按此增加數量</button>
</div>
```
JS:
```javascript=
import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.9/vue.esm-browser.js';
createApp({
data(){
return{
price: 100,
quantity: 1,
}
},
computed:{
discount(){
if(this.quantity > 1){
console.log('Quantity被改變了!')
}
return this.price * 0.8
}
},
methods:{
// 按按鈕改變quantity的值
addQuantity(){
this.quantity += 1;
}
},
})
.mount('#app')
```
codepen: https://codepen.io/alysachan/pen/PopeWqo?editors=1011
答案:會。
雖然畫面沒變,但在codepen的console裏你會看到有出現`Quantity被改變了!`,代表`discount`有被觸發。
因為`quantity`在`data`屬性裏,而在`discount`這個方法裏,官方所指的「响应式依赖」,就包括`quantity`和`discount`。即使`quantity`不在`return`裏,`quantity`也是`discount`這個`computed`方法的其中一個依賴(computed reactive dependencies),
所以`quantity`一改變,`discount`就會被觸發。
那麼如果`quantity`不在`data`裏呢?答案就是不會。
codepen: https://codepen.io/alysachan/pen/VwpxPKN?editors=1111
看看JS部分:
```javascript=
import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.9/vue.esm-browser.js';
// 把quantity從 data移出來
let quantity = 1;
createApp({
data(){
return{
price: 100,
quantity: 1,
}
},
computed:{
discount(){
if(quantity > 1){
console.log('Quantity被改變了!')
}
return this.price * 0.8
}
},
methods:{
addQuantity(){
quantity++;
console.log(`目前quantity的值:${quantity}`)
}
},
})
.mount('#app')
```
結果:console不會出現`Quantity被改變了!`,代表`discount`沒有執行。
詳細解說:
https://forum.vuejs.org/t/how-vuejs-knows-the-dependencies-of-computed-properties-for-caching/4945
## 不可以寫入的意思?
> 知識點:
> - `get()`是取值,採用方法是`getter`函式
> - `set()`是寫入值`setter`函式
預設`computed`是只有`get`,沒有`set`,因為不能寫入值。
範例:https://codepen.io/alysachan/pen/JjWvawX?editors=1111
結果:`discount`不會被改變,而且會跳警告
![](https://i.imgur.com/UR0sy24.png)
![](https://i.imgur.com/T1FaOBe.png)
### 容易誤會的地方
不可寫入值,不是指你不能在`computed`函式裏修改`data`裏的值,你在`computed`函式裏改變在`data`裏任何的值是沒問題的:
試試改動例子中input裏的內容,就會看到購買數量的值都會被+1。因此我們在computed裏修改在data裏的值是沒有問題
示範: https://codepen.io/alysachan/pen/wvJjENx
所以,不可寫入值,是指不可寫入值到`computed`,因為它並沒有`set`函式,預設只有`get`函式使用。為什麼沒有`set`函式(即是setter)就不能寫入值?
因為`set`是一個**可帶入參數**的函式,但`get`函式不容許帶入參數。以下簡單示範`get`和`set`的用法:
https://codepen.io/alysachan/pen/gOmzdJe?editors=0011
```javascript=
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
```
**所以,不可寫入值,更精準的講法是:不能帶參數進去`computed`。**
## 手動加上`set`方法在`computed`裏
雖然`computed`本身沒有`set`方法,但我們可以手動加進去執行看看。把`discount`改成物件,再在裏面建立`get`和`set`方法。
示範:
https://codepen.io/alysachan/pen/LYWmbgm
## 額外知識,在原生JS使用`set`和`get`來實現寫入值前的驗證
```javascript=
let user = {
get name() {
return this._name;
},
set name(value) {
if (value.length < 4) {
alert("Name is too short, need at least 4 characters");
return;
}
this._name = value;
}
};
user.name = "Pete";
alert(user.name); // Pete
user.name = ""; // Name 太短了……
```
例子摘自:https://zh.javascript.info/property-accessors
## computed與methods的分別
| computed | methods |
| -------- | -------- |
| 如果`computed`的響應式依賴沒有改動,就不會觸發。|每次觸發`methods`,`methods`裏的函式一定會執行|
|有緩存資料的功能|沒有緩存資料的功能|
|不可帶入參數|可帶入參數|
|可在HTML裏直接使用該computed函式所回傳的值,因為computed函式本身是有get函式,最後一定會回傳一個值|methods本身沒有規定一定要回傳一個值
關於最後一點,打開Vue dev tool看就知道,`computed`本身就會回傳一個值,但`methods`就不會。
示範:
https://codepen.io/alysachan/pen/WNpgxeW?editors=1111
以上例子可見,如果要把`computed`得出的值寫在畫面上,我們可直接把該computed函式寫在畫面裏,即是`折扣價 {{ computedPrice }} 元`。但`methods`就要加上括號`折扣價 {{ methodsPrice() }} 元`
## computed與watch的分別
| computed | watch |
| -------- | -------- |
| 監聽資料變動產生**資料** | 監聽資料變動產生**行為** |
| 能監聽多個值的變動(只要該資料是在該computed函式裏和data屬性裏)|只能監聽一個值
| 不能帶入參數 | 能帶入參數,第一個參數是新值,第二個參數是舊值|
## 緩存資料的意思
`computed`本身有緩存資料的功能,如果緩存資料沒更動,該`computed`的函式就不會被執行。相反,`methods`沒有緩存功能,因此只要`methods`的函式被觸發,它就會執行。
`computed`緩存例子:
當按按鈕後`num`變成1,即使你之後再按多少次按鈕,`add`都不會被執行
https://codepen.io/alysachan/pen/LYWmOLq
`methods`沒有緩存的例子:
即使`num`變成1,之後你再按按鈕,`add`都會執行
https://codepen.io/alysachan/pen/BaWxmYz
好處:
緩存資料的好處就是省略執行不必要的計算,尤其是在處理大量資料時,緩存資料的功能有助提高效能