Vue.jsでSPAなBingoカードアプリを作ってみてわかったこと

背景
====

https://github.com/ma2shita/bingocard-spa

[SORACOM UserGroup Tokyo #5](https://soracomug-tokyo.connpass.com/event/45943/)の企画としてビンゴにしようっ！て事になったのですが、手軽なビンゴシステムが無かったので「フロントエンジニアとしてもコードが書けるんだぞっ」ということを証明するためにも、とりあえず作ってみました

<img width="375" alt="BINGO.png" src="https://qiita-image-store.s3.amazonaws.com/0/22754/5d3d634e-b0c1-e83f-a74f-a1539dcf923e.png">


材料
====

- [Vue.js](https://jp.vuejs.org/) 2.0
    - [vue-router](https://github.com/vuejs/vue-router)
    - [vuex](https://github.com/vuejs/vuex)
        - [vuex-persistedstate](https://github.com/robinvdvleuten/vuex-persistedstate)
- [Underscore.js](http://underscorejs.org/)
- [seedrandom](https://github.com/davidbau/seedrandom)
- [spectre.css](https://picturepan2.github.io/spectre/)
- [Animate.css](https://daneden.github.io/animate.css/)

vue-routerは、Vue.jsでSPAなアプリケーション作るのに必須
vuexは、シード値(後述)やシートの配列、選択状況の状態を保持するために使用
vuex-persistedstateは、vuexの状態をLocalStorageに保存して途中中断(ブラウザ閉じじゃった・リロード)に対しての堅牢性向上のために使用
Underscore.jsは、`sample` や `range`、`zip` 等、主に2次元配列の操作に使用
seedrandomは、同じシードなら同じ乱数が返却されるようにするために使用
spectre.cssは、軽量なCSSフレームワーク
animate.cssは、タップした時の「ぼよん」アニメーションで使用

構成
----

基本的には vuex にビンゴカードの状態を記録させておき、状態変化をトリガーに表示を変更するという、いわゆるリアクティブな構成となっています

手順
----

vue-cliを使ってベースのscaffoldを作り、それに肉付けします
メインで編集するのは `src/` ディレクトリの中身になります

```console
$ npm install -g vue-cli
$ vue init webpack bingocard-spa
$ cd bingocard-spa
$ npm install underscore --save
... (各ライブラリインストール)
```

ポイント
=======

### vuex の mutation 内で状態(変数)を書き換えても、状態が変化しない場合がある

プリミティブな変数は代入演算子で状態(変数)の変更をするとリアクティブにビューが更新されるのに、配列やオブジェクト(ハッシュ)はビューが更新されない場合がある

一般的な例とダメな時、それに対応する方法を記載しておきます

```javascript:vuex/store.js
const mutations = {
  generateCase: (state, payload) => {
    state.hoge = 10  // 一般的にはこのケース。プリミティブな変数の状態変更でリアクティブにビューが更新される
  },
  ngCase: (state, payload) => {
    var r = payload['r']
    var c = payload['c']
    var v = payload['v']
    state.selection.map[r][c] = v // v を代入して状態を書き換えたのにビューが更新されない
  },
  okCase: (state, payload)
    var r = payload['r']
    var c = payload['c']
    var v = payload['v']
    Vue.set(state.selection.map[r], c, v) // Vue.set を使って書き換えるのが正解 
  }
}
```

ちゃんと[ドキュメントに書いて](https://jp.vuejs.org/v2/guide/list.html#配列の置き換え)ありました
配列やオブジェクト(ハッシュ)は、直接書き換えてはダメだったんですね。ムズカシイ

### vuex-persistedstateは便利

組み込むだけでvuexの状態をLocalStorageに保存してくれます。もちろん状態が更新された瞬間にLocalStorageも更新してくれるほか、ページのロード時における状態のリストアも自動です

ブラウザのクローズやリロードにとても強くなります

#### 組み込み方法

```javascript:vuex/store.js
import createPersistedState from 'vuex-persistedstate'

const store = new Vuex.Store({
  ...
  plugins: [createPersistedState()]
})
```

デプロイ
-------

今回のためだけにrootディレクトリを消費するのも気が引けたため、サブディレクトリを作ってデプロイしました

1. `config/index.js` の `build` における `assetsPublicPath` を `/subdir` にすると http://hostname/subdir で動くようになります
2. `/static` は rootディレクトリに置く必要があります

### デプロイ先

[GitHub Pages](https://pages.github.com/)がいちばん手っ取り早かったです

```console
$ cd bingocard-spa
$ npm run build
$ cp -rp dist/ /any/USERNAME.github.io/subdir/
$ cd /any/USERNAME.github.io/subdir/
$ ln -s subdir/static .
$ git add --all
$ git commit -m "any"
$ git push
```

ちなみにAmazon S3は `/` を index.html にマッピングする方法がすぐにわかりませんでした。それができればS3でも簡単にデプロイできます

イマイチわかって無いところ
======================

### CSSライブラリの読み込み方
結局 `/static/` にダウンロードしてから index.htmlの `<link>` タグで直接読み込んでしまった

結局わかったこと
==============

フロントエンジニア、無理だわ。