iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
自我挑戰組

Vue.js 從零開始系列 第 16

Vue.js 從零開始:computed

  • 分享至 

  • xImage
  •  

此篇後面會有點難懂,可以去看我的範例,更了解實際運作喔,各位加油!

computed 計算屬性

computed大部分都是讀取data資料經過運算後,在渲染到畫面上:
https://ithelp.ithome.com.tw/upload/images/20210925/20118347AyqqYdxLu5.png

使用computed的好處是能讓模板看起來簡潔:

<div id='app'>
  <div>{{ one + two + three }}</div> //未使用computed
  <div>{{ run }}</div> //使用computed
</div>
const vm = Vue.createApp({
  data() {
    return {
      one: 1,
      two: 2,
      three: 3
    };
  },
  computed: {
    run() {
      return this.one + this.two + this.three;
    }
  }
});

vm.mount("#app");

{{ one + two + three }} 可以改成{{ run }},如果是使用methods就要改成{{ run() }},另外使用computed時開頭一定要加上return


computedmethods 的差異?

<div id='app'>
  <div>
    <p>methods: {{ num() }}</p>
    <p>methods: {{ num() }}</p>
    <p>methods: {{ num() }}</p>
    <p>computes: {{ run }}</p>
    <p>computes: {{ run }}</p>
    <p>computes: {{ run }}</p>
  </div>
</div>
const vm = Vue.createApp({
  data() {
    return {
      count: 0,
      four: 4,
    }
  },
  methods: {
    num() {
      console.log('isMethods');
      return this.four + this.count;
    }
  },
  computed: {
    run() {
      console.log('isComputed');
      return this.four + this.count;
    }
  }
});

vm.mount('#app')

console.log:
https://ithelp.ithome.com.tw/upload/images/20210924/20118347ZGWxGr409F.png

模板區塊一樣是各三個,run只有執行一次,而num()卻執行了三次,這是因為computed的特性,computed會將計算過的結果暫存起來,computed的更新條件是原始資料有變更才會更新。

如果有需要計算大量的功能,如果沒有使用computed,每次調用將會消耗大量資源來計算,如果不需要暫存則改用methods代替。

另外computed裡的function無法使用參數。


computed 常用技巧

<div id='app'>
  <input type="search" v-model="search">
  <ul>
    <li v-for="item in filter">
      {{ item.name }}-{{ item.price }}
    </li>
  </ul>
</div>
const vm = Vue.createApp({
  data() {
    return {
      search: '',
      products: [
        {
          name: '上衣',
          price: 500,
        },
        {
          name: '褲子',
          price: 700,
        },
        {
          name: '鞋子',
          price: 2000,
        },
        {
          name: '襪子',
          price: 450,
        },
      ],
    }
  },
  computed:{
    filter() {
      return this.products.filter(item => {
        return item.name.match(this.search);
      });
     },
  }
}).mount('#app')

input輸入框輸入任何字串都會執行迴圈,文字如果部分相符,就會顯示出來:
https://ithelp.ithome.com.tw/upload/images/20210925/20118347F5DX5txe4J.png
filter將每一筆調用出來。
match()當item.name的字串有符合input輸入的文字就調用出來。

JavaScript match()


gettersetter

getter:將data資料取出computed運算完之後渲染到畫面。
setter:把資料(以下範例使用methods)運算完,傳回data

首先computed裡面要改成物件形式(total),在物件裡面新增get()的函式:

<div id="app">
    <ul>
        <li v-for="product in products">
        {{ product.name }} / {{ product.price }}
        <button type="button" @click="addToCart(product)">+</button>
        </li>
    </ul>
    <h3>total 的值:{{ total }}</h3>
    <h3>Computed Getter, Setter</h3>
    computed sum 的值:
    <input type="number" v-model.number="num">
    <button type="button" @click="total = num">更新</button>
    total 的值:{{ total }}<br>
    computed sum 的值::{{ sum }}
</div>
const App = {
    data() {
        return {
            products: [
            {
                name: '上衣',
                price: 300,            
            },
            {
                name: '褲子',
                price: 500,            
            },
            {
                name: '鞋子',
                price: 1500,           
            },
            {
                name: '帽子',
                price: 600,
            },
            ],
            carts: [],
            sum: 0,
            num: 20,
        }
    },
    computed: {
        total: {
            get() {
                let total = 0;
                this.carts.forEach(item => {
                    total += item.price;
                });
                return total;
            }
        },
    },
    methods: {
        addToCart(product) {
            this.carts.push(product);
        },
    }
};
Vue.createApp(App).mount('#app');

input v-model.number="num"已經綁定data的num,預期是點擊更新(@click="total=num")會將total的值變成num
https://ithelp.ithome.com.tw/upload/images/20210925/20118347XgutnBPLcr.png
[Vue warn]: Write operation failed: computed property "total" is readonly。此值只能被讀取。

這時就要使用set()函式,將他加入到computed裡面的total物件:

computed: {
        total: {
            get() {
                let total = 0;
                this.carts.forEach(item => {
                    total += item.price;
                });
                return this.sum || total;
                // 如果 this.sum 有值就回傳,沒有值就使用 total 的值。
            },
            set(val) {
              console.log('val') // num 的20
              this.sum = val; // num 的20,傳回 data 的 sum
            }
        },
    },

CodePen[完整範例]
整個流程:
1.get()會先執行一次,computed會先自己計算一次並暫存。
2.點擊帽子+把金額帶到total,再次觸發get(),total的值變成600。
2.點擊更新觸發set(val)函式,數字20傳回this.sum,同時{{ sum }} 變成 20。
3.最後再次觸發get()把total的值也變成 20。

可以打開CodePen觀察console.log的變化,可以發現gettersetter是分開進行的。


如上述有任何錯誤,歡迎指教,感謝你的收看,明天見。

/images/emoticon/emoticon14.gif


參考資料

六角學院
重新認識 Vue.js
Vue.js


上一篇
Vue.js 從零開始:methods
下一篇
Vue.js 從零開始:watch
系列文
Vue.js 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Oo_花之舞__oO
iT邦新手 1 級 ‧ 2023-03-22 19:03:40

神等好文章 邏輯清楚 易懂易學

approachable

magic0308 iT邦新手 2 級 ‧ 2023-03-27 14:01:03 檢舉

謝謝你的肯定

我要留言

立即登入留言