【Vue.js】computedとmethodsとwatchの使い分け

以下3つのオプションの区別が曖昧だったので、メモしておきます。
ソースコードは[公式サイト](https://jp.vuejs.org/)よりお借りしました。

# computedオプション
- 算出プロパティ
- リアクティブな依存関係にもとづきキャッシュされる
- リアクティブな依存関係が更新されたときにだけ再評価されるので、逆に言えばリアクティブな依存がない場合は二度と更新されない
- ゲッター(getter関数)とセッター(setter関数)の両方が利用できる（デフォルトはゲッターのみ）

## ゲッターとセッター
computedでセッターを使用した場合、dataオプションで設定されたプロパティを更新することもできます。

[ゲッターとセッターの定義](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters)
### ゲッター
特定のプロパティ値を取得するためのメソッド
### セッター
特定のプロパティ値を設定するためのメソッド

## ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こりますが、nowプロパティはリアクティブな依存関係にないため、最初に読み込まれた以降は変化しません。

```vue_test01.html
<!DOCTYPE>
<head>
  <meta charset="UTF-8">
  <title>Vue.js_test01</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <input v-model="firstName" placeholder="firstName">
    <input v-model="lastName" placeholder="lastName">
    <p>firstName: {{ firstName }}</p>
    <p>lastName: {{ lastName }}</p>
    <p>fullName: {{ fullName }}</p>
    <p>now: {{ now }}</p>
  </div>

  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              firstName: '',
              lastName: ''
          },
          computed: {
              // fullName/now: 算出プロパティ名
              fullName: function () {
                  return this.firstName + ' ' + this.lastName
              },
              now: function () {
                  return Date.now()
              }
          }
      })
  </script>
</body>
```

# methodsオプション
- メソッド
- 再描画が起きると常に関数を実行する

## ソースコード
firstNameプロパティやlastNameプロパティを変更すると再描画が起こるので、それに合わせてnowプロパティも変化します。

```vue_test01.html
<!DOCTYPE>
<head>
  <meta charset="UTF-8">
  <title>Vue.js_test01</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <input v-model="firstName" placeholder="firstName">
    <input v-model="lastName" placeholder="lastName">
    <p>firstName: {{ firstName }}</p>
    <p>lastName: {{ lastName }}</p>
    <p>fullName: {{ fullName() }}</p>
    <p>now: {{ now() }}</p>
  </div>

  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              firstName: '',
              lastName: ''
          },
          methods: {
              fullName: function () {
                  return this.firstName + ' ' + this.lastName
              },
              now: function () {
                  return Date.now()
              }
          }
      })
  </script>
</body>
```

# watchオプション
- 監視プロパティ
- 既にセットされているプロパティを監視する
- ``監視するプロパティの名前``と、そのプロパティに変化(トリガー)があった場合に``実行する関数(ハンドラ)``を対にして指定する
- 関数は、更新後・更新前のプロパティの値を引数に取ることができる
- 処理は実行するが、データは返さない
- computedでは行えないコストの高い処理を実行できる

以下、公式サイトより。
>この場合では、watch オプションを利用することで、非同期処理( API のアクセス)の実行や、処理をどのくらいの頻度で実行するかを制御したり、最終的な answer が取得できるまでは中間の状態にしておく、といったことが可能になっています。これらはいずれも算出プロパティでは実現できません。

## ソースコード
fullNameプロパティの表示はwatchオプションで描画することもできますが、逆に冗長なコードになってしまいます。

```vue_test01.html
<!DOCTYPE>
<head>
  <meta charset="UTF-8">
  <title>Vue.js_test01</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <input v-model="firstName" placeholder="firstName">
    <input v-model="lastName" placeholder="lastName">
    <p>firstName: {{ firstName }}</p>
    <p>lastName: {{ lastName }}</p>
    <p>fullName: {{ fullName }}</p>
    <p>now: {{ now }}</p>
  </div>

  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              firstName: '',
              lastName: '',
              // プロパティはあらかじめセットしておく
              fullName: '',
              now: '',
          },
          watch: {
              // firstName/lastName/now: 監視対象のプロパティ名
              // newValue: 更新後のプロパティの値
              // oldValue: 更新前のプロパティの値
              firstName: function (newValue, oldValue) {
                  console.log(newValue, oldValue);
                  this.fullName = newValue + ' ' + this.lastName
              },
              lastName: function (newValue, oldValue) {
                  console.log(newValue, oldValue);
                  this.fullName = this.firstName + ' ' + newValue
              },
              // 監視しているが、値に変化がないので変更されることはない
              now: function () {
                  return Date.now()
              }
          }
      })
  </script>
</body>
```

# 参考サイト
[リアクティブの探求](https://jp.vuejs.org/v2/guide/reactivity.html)
[【Vue.js】v-modelを使ってた時になんか動なかったお話](http://one-way.tech/programing/v-reactive/)
[算出プロパティとウォッチャ](https://jp.vuejs.org/v2/guide/computed.html)