Vue.js2 computedの使い方、使いどころについて

# はじめに
Vue.jsを触り始めた時`data`や`methods`の使い方はわかりやすかったのですが、`computed`の用途はよくわからなかったので、同じような方の役に立てればと思い記事を書きました。

こちらの記事はVue.js初心者向けの内容になっております。

# 結論
まず結論ですが、`computed`は以下のような場合に使うと良いと思います。
* 定数を定義したい
* 計算した結果を返すような処理を書きたい

それでは詳細な説明に入っていきます。

# 目次
[1.computeの特徴](#1-computeの特徴)
[2.computedの使用例](#2-computedの使用例)
[3.methodsとの比較](#3-methodsとの比較)
[4.おわりに](#4-おわりに)
[5.参考](#5-参考)


# 1. computeの特徴
* 返り値が必要
* 「リアクティブな値を参照していない場合」再実行されることはない
* 「リアクティブな値を参照している場合」参照している値が更新されたときのみ再実行される
* setterを使用しない場合、読み取りしかできない
* setterを使用しない場合引数を取れない
※リアクティブな値とは変更が検知されるような値です(`data`プロパティに定義した値など)

このような特徴から「返り値のある処理」、「値の変更を検知して再実行したい処理」などに使用できます。

# 2. computedの使用例
次に使用例をいくつか書いていきます。

## 2.1 定数を定義したい
１つ目は定数を定義したい時です。

`computed`にリアクティブでない値を返すように定義すれば変更できない値になり定数のように扱えます。

▼定数のように扱う例です
```
<template>
  <div style="margin: 40px">
    computedが返す値A：{{ computedValueA }}
  </div>
</template>

<script>
export default {
  computed: {
    computedValueA() {
      return 'computedValueA' // 返り値を直接書く
    }
  }
}
</script>
```
返り値を直接`computed`に書いてしまえば変更できない値になるので定数として扱うことができます。

`data`プロパティなどで定義した値はリアクティブになりますので`computed`で返しても定数のようには扱えません。

## 2.2 計算した結果を返すような処理を書きたい
2つ目ですが、こちらがメインの使いどころになると思います。
配列をフィルタリングして返したり、判定結果を返したり様々な場面で使用します。
計算して結果を返すようなものであれば基本的に`computed`で書くと良いと思います。

## 2.2.1 配列をフィルタリングして返す
配列に入った数字から偶数のものだけをフィルタリングして表示しています。
```
<template>
  <div style="margin: 40px">
    元の状態：{{ numList }}<br />
    フィルタリングした状態：{{ filtereEven }}
  </div>
</template>

<script>
export default {
  data: () => ({
    numList: [1,2,3,4,5,6,7,8,9,10]
  }),
  computed: {
    filtereEven() {
      return this.numList.filter((num) => num % 2 === 0)
    }
  }
};
</script>
```
![computed使用例-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/472cbf8e-870e-ad46-9d40-40289ca4fb99.png)
バックエンドから受け取った配列データをフィルタリングするなど、よくある使い方です。


## 2.2.2 判定結果返す
判定処理を書きたい場合にもよく使います。

以下のような処理を`computed`で書きました。
１）奇数か偶数か判定する処理(`isEvenNum`)
２）１）の判定によって表示が切り替わる処理(`oddOrEven`)
```
<template>
  <div style="margin: 40px">
    <div>{{ oddOrEven }}</div>
    <button @click="incrementNum()">numに1を足す</button>
  </div>
</template>

<script>
export default {
  data: () => ({
    num: 1
  }),
  computed: {
    isEvenNum() {
      return this.num % 2 === 0
    },
    oddOrEven() {
      return this.isEvenNum ? 'numは偶数です。' : 'numは奇数です。'
    }
  },
  methods: {
    incrementNum() {
      this.num = this.num + 1
    }
  }
};
</script>

<style scoped>
button {
  background: #eee;
  border-radius: 3px;
  position: relative;
  max-width: 280px;
  padding: 10px 25px;
  color: #313131;
  transition: 0.3s ease-in-out;
  font-weight: 500;
}
</style>
```
画面読み込み時の状態です。
![computed-odd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/474ecad6-c32a-4ca4-fd40-cac8bb00c273.png)
ボタンを押すと表示が切り替わります。
![computed-even.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/c30c5f2e-55c3-c050-6a2d-2c5c1fe3cbd4.png)

# 3. methodsとの比較
実は具体例として出したような処理であれば`methods`でも全く同じことができます。
しかし`computed`と`methods`では再実行される条件として以下のような違いがあります。
computed：参照する値が変更された時のみ再実行される
methods：画面が再描画されるたびに再実行される
`methods`は画面が再描画されるたびに再実行されてしまうため余計な実行が増えてしまいます。

文章だけだとわかりにくいと思うので実際に例を見ていきましょう。
```
<template>
  <div style="margin: 40px">
    computedNum：{{ computedNum }}<br />
    methodsNum：{{ methodsNum() }}<br />
    numB：{{ numB }}<br />
    <button @click="addOneNumA()">NumAに1を足す</button>
    <button @click="addOneNumB()">NumBに1を足す</button>
  </div>
</template>

<script>
export default {
  data: () => ({
    numA: 1,
    numB: 1
  }),
  computed: {
    computedNum() {
      console.log('computed')
      return this.numA
    }
  },
  methods: {
    methodsNum() {
      console.log('methods')
      return this.numA
    },
    addOneNumA() {
      this.numA = this.numA + 1
    },
    addOneNumB() {
      this.numB = this.numB + 1
    }
  }
};
</script>

<style scoped>
button {
  background: #eee;
  border-radius: 3px;
  position: relative;
  max-width: 280px;
  padding: 10px 25px;
  color: #313131;
  transition: 0.3s ease-in-out;
  font-weight: 500;
}
</style>
```
`NumA`は`computed`と`methods`が参照しています。
`NumB`はどちらにも参照されていません。
`NumA`、`NumB`を変更した際にどういった挙動をするのか確認します。

## 3.1 画面を読み込み時
まずは画面を読み込んだ際の挙動ですが、どちらも実行されています。
![computed_methods_比較.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/9efda448-dd69-e737-3478-b1e5cab04cc3.png)

## 3.2 「NumA」に変更があった場合
「NumAに1を足す」ボタンを押して`NumA`の値が変更されました。
`computed`、`methods`共に実行されています。
![methods_computed_比較2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/6628b7ee-f143-8403-4f29-2868ad770d29.png)

## 3.3 「NumB」に変更があった場合

「NumBに1を足す」ボタンを押して`NumB`の値が変更されました。
`methods`のみ実行されています。
これは`NumB`の値が変更され再描画されたためです。 
`computed`は`NumA`に変更がないため再実行されません。
![computed_methods_比較3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1046681/afeb0f3e-7aa5-846f-d2ae-08ddc7134dce.png)

## 3.4 「methodsとの比較」まとめ
`methods`を使用すると余計に再実行されてしまいますので、「計算結果を返す処理」は`computed`で行いましょう。

# 4. おわりに
`computed`は使用頻度は高いのでぜひマスターしていただければと思います。
記事を見ていただきありがとうございました。
# 5. 参考
https://jp.vuejs.org/v2/guide/computed.html

https://qiita.com/NabeKz/items/31da569faf45c4437c90