某案件で Vue.js + Vuex + axios を採用しました。備忘録としてまとめます。
前提
それぞれのバージョンは以下の通り。
"dependencies": { "axios": "^0.17.1", "es6-promise": "^4.1.1", "vue": "^2.5.2", "vue-router": "^3.0.1", "vuex": "^3.0.1" }
開発には Visual Studio Code を利用。以下のプラグインを入れると便利。
- HTML Snippets
- vetur
- VueHelper
vue-cli
vue-cli の webpack テンプレートを利用。当初、イチからプロジェクトを準備しようと思っていたが、vue-cli で作成した方がいろいろと確実と思われたので。ハマりどころも多いかもしれないが...。
webpack / source maps
source maps については、webpack の 公式ドキュメント と以下が参考になりました。
devtool の指定によってマッピングファイルの生成有無や中身、js ファイルのサイズが変わってくる模様。試したところ以下のような感じ。
デバッグ△は変数名が minify される程度、js サイズ△は kByte 程度。(分かり辛い...)
devtool | デバッグ | js サイズ | source maps |
---|---|---|---|
source-map | △ | ◯ | あり |
eval-source-map | ◯ | ✕ | なし |
inline-source-map | △ | ✕ | なし |
cheap-eval-source-map | △ | △ | なし |
eval | ✕ | △ | なし |
minify なし | △ | △ | − |
結局、開発環境向けには eval-source-map
を指定、プロダクション環境向けには source-map
を指定。
その他、webpack のちょっとした小細工は以下を参照。
vue-cli の webpack テンプレートでテスト環境向けにビルドしたい - kntmr-blog
プロジェクト構成
├─build ├─config ... 設定ファイル ├─dist ├─node_modules ├─src ... エントリーポイントの js を配置 │ ├─api ... API アクセス │ ├─components ... 共通コンポーネント / 基底コンポーネント │ ├─pages ... ページコンポーネント │ ├─router ... ルーティング (今回は利用しない予定) │ └─store │ └─modules ... 状態管理 ├─static └─test ... テストコード
このあたりは Vuex の examples などを参考にしました。shopping-cart とか。
ページコンポーネントはアプリケーション内で単一となるコンポーネント。共通コンポーネントや基底コンポーネントはアプリケーション内で使い回されるコンポーネント。共通コンポーネントは複数の基底コンポーネントで構成される。
共通コンポーネントや基底コンポーネントに id 属性を付けると、ページ内で id が重複する可能性があるため注意。ページコンポーネントには id 属性、共通コンポーネントと基底コンポーネントには class 属性を指定するといいかもしれない。
スタイルガイド
Vue.js の スタイルガイド は必読。
store の namespace
複数の store に分割するときは、namespace を指定してモジュールを管理する。
export default { namespaced: true, state, getters, mutations, actions }
モジュール間でデータを受け渡す場合は dispatch する。(この方法がベストかどうかはちょっとあやしい...)
API
axios に依存するコードをまとめて抽象化する。ここには業務的なロジックは実装しない。ここで export した関数を store の actions から呼び出す。コンポーネント内には API 呼び出しの処理は書かない。
mixins / filters
mixins は Vue コンポーネントの実装を共通化する用途で使う。今回はローディングやエラー処理など、各画面共通で使う computed
や methods
の実装を mixins にまとめた。filters は日付やテキストをフォーマットする用途で使う。
反省点など
コンポーネントの命名規則がイマイチ
このコンポーネントがどういう役割を持っているのか名前から判断できるとよい。そのためには単一責務のコンポーネントとして切り出すようにするのが理想と思われる。
再掲: スタイルガイド
actions / mutations / state
今回、1つの action の中では基本的に1つの mutation を呼び出すような形で mutations を書いたが、ある action の中でどの state が書き換わるのか分かり辛い感じになってしまった。ある程度、state の粒度に合わせて mutations を書いた方がいいのかもしれない。
API とコンポーネントの分離
API の項にも書いたが、API の変更をコンポーネント側に波及させたくなかったので、コンポーネント内に API 呼び出しの処理は書かないことにした。が、結局、API のレスポンスオブジェクトがコンポーネントのあちこちで参照されていたため、このあたりはきれいに分離できなかった。
API とコンポーネントを分離するために、store 内で変換するような仕組みを持たせてもよかったかもしれない。ただ、store の責務がどんどん膨らんでしまうのが懸念。API と store の間にもう1レイヤあるとよかったのだろうか。
デザインとコンポーネントの設計
当たり前だが、デザインとコンポーネントは密接に関係する。きれいなコンポーネント設計にするためには、当然、それを意識したデザインを作らないといけない。
今回、開発体制的な理由で基本的にデザインはこちらでコントロールできるものではなかったため、一部のコンポーネントに重複や無駄が入り込んでしまった。が、これはしょうがないのかもしれない。
IE 対応
babel や es6-promise は使っていたものの、Edge では動くが IE10 では動かないということがあった。が、npm update
してパッケージをアップデートしたら動くようになった...。詳しくは追い切れていないが、babel あたりの更新が要因か。
割と短期間で挙動が変わるような更新があるのは、ちょっと腑に落ちないところがある。このあたりもきちんと追えるようにしたい。
テストコード
今回、Avoriaz や Sinon.JS でコンポーネントのテストコードを作成する予定だったが、メンバーの教育に時間がかかりそうだったため諦めることにした。(その代わり社内で使われている画面系のテスト仕様書でテストした)
まとめ
Vue.js の特長として、シンプルな構成で、かつ拡張性が高いということが挙げられる。また、ミニマムにスタートできるメリットがある。ただ、この ミニマム にフォーカスされるためか、世間からは「小規模向け」というイメージを持たれているように思える。(主観ですが...)
決してそんなことはなく、小規模なアプリケーションはもちろん、大規模なアプリケーションにも十分適用できると思われる。