# 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 好處: 緩存資料的好處就是省略執行不必要的計算,尤其是在處理大量資料時,緩存資料的功能有助提高效能