サーバレスアプリ開発をjQueryでがんばらないためのVue.js

サーバレス構成のWebアプリ開発を担当し、そこでVue.jsを使ってみたので採用した経緯と、Vue.jsの何を使ったかを紹介する。

# これまでのWebアプリ開発

JavaのWebアプリ開発ならThymeleafやFreemarkerなどのテンプレートエンジンを使用していた。
サーバレスだと、HTMLとJavaScriptで表示の処理を行うため、JavaScriptの処理が増える。
よく使われるのはjQueryによってDOMを操作し、HTMLを動的に変更する方法だろう。

## jQueryのDOM操作は辛いよ

jQueryを使ったことがあればお分かりだと思うが、jQueryによるDOM操作は「このイベントで、このvalueがxxxなら、このDOMを変更する」という手続き型のコードになる。
JavaScriptの処理が増えると、この手続き型のコードが増えてデータと状態の管理が煩雑になる。
また、JavaScript側に表示の処理があるためHTMLを簡単に変更できない。

## そこでVue.jsですよ

SPAっぽい要件なので、SPAに適したJavaScriptフレームワークを使う方がよさそう。
SPAがやりたいわけではないが。

Vue.jsは社内で検証の候補になっていたので採用してみた。
公式ガイドをさらっと読むと、HTMLに値を埋め込める、条件分岐、ループを組み込めるので、テンプレートエンジンと同じことができそう。
クライアントの開発は一任されていたので、試しやすかったのもある。

# Vue.jsのはじめかた

お手軽にはじめるなら `script` タグに1行追加するだけ。

```html:HTML
<!-- 開発バージョン(警告出力とデバッグモードあり) -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
```

```html:HTML
<!-- 本番バージョン(警告出力なし、圧縮済み -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

但し、webpackでビルドが必要な機能は使えない。例えば単一ファイルコンポーネント。
単一ファイルコンポーネントはHTMLを構成するヘッダ、ボディなどを部品ごとに分けて扱える。
便利ではあるが、Node.jsのツール(npm, webpack, vue-cli)が必要になる。
公式ガイドでは、最初は `script` から使用することをおすすめしている。
機能数も少ないので `script` から使用する。

## Vueのアプリ開発

### Vueインスタンスの作成

画面ごとにHTMLを分けたので、HTMLごとにルートVueインスタンスを生成。
Vueインスタンスで使用するオプションは `el`, `data`, `methods`, `computed` のみ。

```javascript:JavaScript
var vm = new Vue({
  el: '#app',
  data: {
    items: [
      {
        isbn: '4873118069',
        title: 'サーバーレスシングルページアプリケーション',
        publisher: 'オライリージャパン',
        price: 2808
      },
      {
        isbn: '4798155160',
        title: 'AWSによるサーバーレスアーキテクチャ',
        publisher: '翔泳社',
        price: 3888
      }
    ]
  },
  methods: {
    // メソッド
  },
  computed: {
    // 算出プロパティ
  }
})
```

<dl>
  <dt>el</dt>
  <dd>Vueインスタンスの対象となるHTML要素を指定する。</dd>
  <dt>data</dt>
  <dd>バインドする変数を格納する。変数の値が変更されるとHTMLに反映される。</dd>
  <dt>methods</dt>
  <dd><code>v-on</code> で呼び出す関数を記述する。</dd>
  <dt>computed</dt>
  <dd>算出プロパティ。依存するものが更新されなければ以前計算された結果を返す。</dd>
</dl>

### HTMLにJavaScriptの値を埋め込む

Vueインスタンスの `data` の変数を `{{変数}}` のように2重の中括弧で囲むとHTMLに値が埋め込める。
変数を処理させて表示する場合は算出プロパティを使用する。

例えば、合計金額を算出して表示するには以下のように記述する。

```javascript:JavaScript
var vm = new Vue({
  el: '#app',
  data: {
    items: [
      {
        isbn: '4873118069',
        title: 'サーバーレスシングルページアプリケーション',
        publisher: 'オライリージャパン',
        price: 2808
      },
      {
        isbn: '4798155160',
        title: 'AWSによるサーバーレスアーキテクチャ',
        publisher: '翔泳社',
        price: 3888
      }
    ]
  },
  methods: {
    // メソッド
  },
  computed: {
    priceTotal: function() {
      var total = 0;
      for (var item in items) {
        total += item.price;
      }
      return priceTotal;
    }
  }
})
```

```html:HTML
合計金額：{{priceTotal}}
```

### ディレクティブの使い方

Vue.jsはHTMLタグの中にディレクティブという特別な属性を埋め込むことでHTMLを制御する。
使用したディレクティブは以下の通り。

### `v-if`

条件で表示を切り替えるのに使用する。

`v-show` も同じ用途で使用されますが、`v-show` はCSSの `display: none` で表示を隠すのと同じでDOMは消えない。
DOMの追加、削除がない分、描画は `v-show` の方が早い。

例えばログインユーザの権限で表示項目を変える場合はDOMに出したくないから `v-if` の方がよさそう。
プルダウンやラジオボタンで項目を切り替えるのであれば、`v-show` の方がよさそう。

### `v-for`

`data` の配列データを1件ずつループで処理するのに使用する。
例えばAjaxでDBから取得したデータを一覧表示する場合に使用される。
一覧を10件ずつ表示する場合は算出プロパティを使用するとよさそう。

```javascript:JavaScript
var vm = new Vue({
  el: '#app',
  data: {
    items: [], // AjaxでDBから取得
    pagination: {
      page: 0,
      pageSize: 10,
    },
    startPage: 0,
    pageSize: 10,
  },
  methods: {
    // メソッド
  },
  computed: {
    pageItems: function () {
      var startPage = this.pagination.page * this.pagination.pageSize;
      return this.items.slice(startPage, startPage + this.pagination.pageSize);
    },
  }
})
```

```html:HTML
<table>
  <thead>
    <tr>
      <th>ISBN</th>
      <th>タイトル</th>
      <th>出版社</th>
      <th>値段</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="item in pageItems">
      <td>{{item.isbn}}</td>
      <td>{{item.title}}</td>
      <td>{{item.publisher}}</td>
      <td>{{item.price}}</td>
    </tr>
  </tbody>
</table>
```

### `v-bind`

タグの属性に値を埋め込むのに使用する。
例えば `class` の属性を動的に切り替えるには `<div v-bind:class="disabled: isDisabled"></div>`のように記述すると `data` の `isDisabled` がtrueの場合に `disabled` が埋め込まれる。
`v-bind:class` は `:class` のように省略して記述することもできる。

また、`v-for` と組み合わせて動的にセレクトボックスを作ることができる。

```html:HTML
<select>
  <option v-for="item in items" :value="item.isbn">{{item.title}}</option>
</select>
```

### `v-on`

イベントで `methods` に記述した関数を呼び出すのに使用する。
例えばボタンクリック時のイベントは `v-on:click="ファンクション名"` のように記述する。
また、`@click="ファンクション名"` のように省略して記述することもできる。

### `v-model`

`input`, `select`, `textarea` の値を `data` の変数にバインドするのに使用する。

### `v-cloak`

`v-cloak` を使うことで、値が埋め込まれるまで `{{変数}}` がそのまま表示されるのを防ぐことができる。
CSSで `display: none` を適用しておきます。

``` css:CSS
[v-cloak] {
  display: none;
}
```

# まとめ

サーバレス構成だとJavaScriptフレームワークは必須かなと思う。
ReactやAngularは分からないが、Vue.jsは使いやすい。
何よりDOM操作から開放されることがよい。