Vue.jsの便利な機能coumptedとは？

# 前提
Vue.jsを使い始めて。便利だな〜と最近重宝しているのが**算出プロパティ（computed）**という仕組みです。
メソッドとは似ているようでも少し違う、computedの基本的な使い方を説明します。

# 算出プロパティ（computed）って何？
フォームのサンプルを使って説明します。
よくある情報登録フォームをVue.jsで作成しました。
入力される値の型チェック、未入力チェック（バリデーション）にcomputedを使っています。
<br>

![スクリーンショット 2017-10-19 11.14.10.png](https://qiita-image-store.s3.amazonaws.com/0/111637/17684b69-6322-74ec-ef49-3218bed698ee.png)


## 実装要件

- 各項目についてバリデーションを実装し、エラーの場合はエラーメッセージ（オレンジ文字色）を各項目部分に表示
- 同じくエラーの場合は、Nextボタンを`disabled`にする
- 必須項目が未入力の場合もNextボタンを`disabled`にする

## computedの実行イメージ
項目をTelに絞って流れを図解してみます。
Telの入力値が**数値ではなかったら**エラーメッセージを表示するロジックです。

![スクリーンショット 2017-10-20 11.44.18.png](https://qiita-image-store.s3.amazonaws.com/0/111637/f36572e6-96df-dd61-5502-5b662abb9f84.png)

Telの入力値は、HTML側でinputタグに`v-model="userInfo.tel"`と設定しているので、JS側では`this.userInfo.tel`で参照できます。これはVue.jsのフォーム入力バインディングの仕組みですね。

やりたいことは、Tel欄の入力時にバリデーションを実行したいので、ここでcomputedの出番です。
computedの仕組みは、**値が変わるとその値に依存しているすべてのバインディングが更新されます。**

つまりここでは、Telの値 `userInfo.tel`が変わると、それに依存しているcomputedの`validateTel`が実行されるのです。
`validateTel`の返り値をみて、HTML側でエラーメッセージを表示するかどうかを`v-show`で設定しています
<br>
挙動を確認してみます。

![github.gif](https://qiita-image-store.s3.amazonaws.com/0/111637/23c33810-fd45-73b5-9f7a-1d2be8b4cb7e.gif)

Telの入力欄に入力する度に`validateTel`が実行されているのがわかるかと思います！

# 実装を見てみる

[サンプルアプリ](https://github.com/kaorina/vue-form-validation) :information_desk_person:

ソースはこちら。（Ruby on Railsで環境を作っているためerbファイルを使っています）

```html+erb:index.html.erb
<div id="app" class="container">
  <h1>Form Example</h1>
  <div class="form-group">
    <label>Email [必須]</label>
    <input v-model="userInfo.email" type="email" class="form-control" placeholder="例) sample@example.com">
    <span v-show="!validateEmail" class="text-warning">正しいEmailを入力してください</span>
  </div>
  <div class="form-group">
    <label>Tel [必須]</label>
    <input v-model="userInfo.tel" type="text" class="form-control" placeholder="例) 0311112222">
    <span v-show="!validateTel" class="text-warning">Telは数値で入力してください</span>
  </div>
  <div class="form-group">
    <label>ユーザー名</label>
    <input v-model="userInfo.userName" type="text" class="form-control" placeholder="例) user-1">
    <span v-show="!validateUserName" class="text-warning">ユーザー名は半角英数字で入力してください</span>
  </div>
  <button :disabled="!validation" type="button" class="btn btn-info">Next</button>
</div>

```

```js:app.js
$(function () {
  new Vue({
    el: '#app',
    data: {
      userInfo: {
        email: '',
        tel: '',
        userName: ''
      }
    },
    computed: {
      validation: function () {
        return (this.validateTel &&
                this.validateEmail &&
                this.validateUserName);
      },
      validateTel: function () {
        var pattern = /^\d+$/; // 1文字以上の整数のみ許容
        return pattern.test(this.userInfo.tel.trim());
      },
      validateEmail: function () {
        var pattern = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
        return pattern.test(this.userInfo.email);
      },
      validateUserName: function () {
        if (this.userInfo.userName.length === 0) {
          return true;
        } else {
          var pattern = /^\w+$/; // 大文字小文字英数字、アンダースコアのみ許容
          return pattern.test(this.userInfo.userName);
        }
      }
    }
  });
});
```

各項目のバリデーション用にcomputedを使い `validateEmail`, `validateTel`, `validateUserName`を定義しています。
全項目のバリデーション用にそれらの返り値を返すだけの`validation`を定義しています。
この`validation`を使ってHTML側ではNextボタンの`disabled`を有効にするかどうかを判定しています。

# computedの使いどころって？
今回の例の他にインクリメンタルサーチ（[こうゆうもの](https://qiita.com/fumix/items/9c8da630de8176669955#%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%82%BF%E3%83%AB%E3%82%B5%E3%83%BC%E3%83%81)）もcomputedを使えば簡単にできるみたいです！

使いどころとしては、**ユーザーからのイベント発生 => 値が変更される => その値に変更があった場合、動的に何かしらの処理を実行したい** 時に使えるのではと理解しています。


# 算出プロパティとメソッドはどう違うの？
少し余談になりますが、computedと似たようなプロパティとしてメソッド（methods）があります。
両者の違いについては[公式ガイドの算出プロパティvsメソッド](https://jp.vuejs.org/v2/guide/computed.html#算出プロパティ-vs-メソッド)がわかりやすいです。

```
** 以下引用 **
算出プロパティの代わりに、同じような関数をメソッドとして定義することも可能です。
最終的には、2つのアプローチは完全に同じ結果になります。

しかしながら、算出プロパティは依存関係にもとづきキャッシュされるという違いがあります。
算出プロパティは、それが依存するものが更新されたときにだけ再評価されます。

** 中略 **
対称的に、メソッド呼び出しは、再描画が起きると常に関数を実行します。
** 中略 **
キャッシングしたくない場合は、代わりにメソッドを使いましょう。
```

メソッドを使っても同じことができそうなので、先ほどのサンプルを編集してみます。
Telのバリデーション部分のみメソッドを使う形に実装してみました。

```html+erb:index.html.erb
<!-- 上記部分省略 -->
  <div class="form-group">
    <label>Tel [必須]</label>
    <input v-model="userInfo.tel" @input="validateTel2" type="text" class="form-control" placeholder="例) 0311112222">
    <span v-show="!validateTel2()" class="text-warning">Telは数値で入力してください</span>
  </div>
<!-- 以下省略 -->
```

```js:app.js
<!-- 上記部分省略 -->
    computed: {
       // ここは変更なし
    },
    methods: {
      validateTel2: function () {
        var pattern = /^\d+$/; // 1文字以上の整数のみ許容
        return pattern.test(this.userInfo.tel.trim());
      }
    }
<!-- 以下省略 -->
```

JS側にメソッド`validateTel2`を定義し、Telのinputタグに@inputを追加しました。ユーザーが`input`する度にメソッドを実行するという実装にしました。
動作しますが、このままでは他の項目（Email等）の`input`タグに入力する際もこのメソッドが実行されてしまいます。。(ﾉ∀｀*)ｱﾁｬｰ
もう少し工夫すれば実装できそうな気もしますが、それを探るよりもcomputedを使ってサクッと実装したほうがベターですね。

# まとめ

- 算出プロパティは依存関係にもとづきキャッシュされるので、依存するものが更新される時のみ実行される。無駄な実行がない。
- 簡潔に記載できるので、可読性が高い。

賢いcomputedを使って、さらにVue.jsを楽しみましょう ^^！

# 参考
[公式ガイドのバリデーションサンプル](https://jp.vuejs.org/v2/examples/firebase.html)