Vue School の無料コース「Vuex for Everyone」を受講した.Vue.js 初心者でも受講できるレベルになっているし,今まで Vue.js を書いたことはあるけど,Vuex はまだ使ったことがないという人にもオススメできる.進め方にもよるけど,写経するとしても2,3時間あれば終わる.
Vuex for Everyone
今回受講した「Vuex for Everyone」の題材は「shopping-cart」で,ステップバイステップに実装を進めていく.動作確認をしながら,機能を追加したり,リファクタリングをしたりする.
動画を見るとわかるけど,とにかく実装スピードが早いので,写経する場合は何度も戻して見直す必要がある.ただ困ったときは Vue School の GitHub にセッションごとにコミットされているので,それを活用すればオッケー.
セッション一覧
- Introduction
- Meet Vuex ⏲ 2:46
- Create a new project with vue-cli ⏲ 3:45
- Install and use Vuex ⏲ 1:57
- Vuex Options
- Vuex Mutations ⏲ 3:22
- Vuex Getters ⏲ 1:57
- Vuex Actions ⏲ 4:25
- Store Access from all Components ⏲ 0:53
- Shopping Cart Features
- Add products to the cart ⏲ 5:08
- Vuex Mutation History and Vue Devtools ⏲ 2:46
- Cart items and total ⏲ 4:32
- Checkout ⏲ 3:49
- Advanced Vuex Options
- Dynamic Vuex Getters ⏲ 2:31
- Vuex Map Helpers ⏲ 8:04
- Split Vuex Store in Multiple Files ⏲ 1:11
- Vuex Modules ⏲ 7:23
- Namespaced Vuex Modules ⏲ 6:27
(受講画面例)
Meet Vuex
- Vuex とは何か?
- なぜ Vuex が必要なのか?
Create a new project with vue-cli
Vue School のボイラープレートから「shopping-cart」プロジェクトを作成する.
$ npm install -g vue-cli $ npm install -g yarn $ vue init vueschool/webpack-template shopping-cart ? Project name shopping-cart ? Project description A Vue.js project ? Author kakakakakku <y.yoshida22@gmail.com> ? Vue build runtime ? Install vue-router? No ? Use ESLint to lint your code? No ? Setup unit tests with Karma + Mocha? No ? Setup e2e tests with Nightwatch? No vue-cli · Generated "shopping-cart". To get started: cd shopping-cart npm install npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack $ cd shopping-cart $ yarn $ yarn dev
次に Vuex の GitHub にあるサンプルを参考に,ProductList コンポーネントを実装する.API 部分はモックになっている.
const _products = [ {"id": 1, "title": "iPad 4 Mini", "price": 500.01, "inventory": 2}, {"id": 2, "title": "H&M T-Shirt White", "price": 10.99, "inventory": 10}, {"id": 3, "title": "Charli XCX - Sucker CD", "price": 19.99, "inventory": 5} ]
この時点で,3商品を表示することができる.
(実装画面)
Install and use Vuex
ここで Vuex をインストールする.
$ yarn add vuex
そして,新しく store/index.js
を作成する.ここで,さっそく Vuex で重要なコンセプトが出てくる.
- state
- getters
- actions
- mutations
Vuex Mutations
ここでは,実際に mutations を実装する.ハンドラ関数の第一引数として state
を指定し,追加でペイロードとして products
を指定している.
// shopping-cart/src/store/index.js mutations: { setProducts (state, products) { // update products state.products = products } }
Vuex Getters
getters の実装はシンプルで,ストアからフィルタして取得できる.実際にモックの1商品の在庫を0にして,動作確認をした.
// shopping-cart/src/store/index.js getters: { // = computed properties availableProducts (state, getters) { return state.products.filter(product => product.inventory > 0) } }
(実装画面)
Vuex Actions
この時点だと,まだコンポーネントから直接 Ajax で API を呼び出しているので,ベストプラクティスではなく,actions を使って解決するとのことだった.最初は,そのまま mutations をコミットすれば良いのではないかと思っていたけど,mutations は同期,actions は非同期なので,ここでは actions を使って,mutations をコミットする必要性を学べた.そして,API を呼び出しているときのローディング画像もここで実装した.
// shopping-cart/src/store/index.js actions: { // = methods fetchProducts ({commit}) { return new Promise((resolve, reject) => { // make the call // call setProducts mutation shop.getProducts(products => { commit('setProducts', products) resolve() }) }) } },
actions を呼び出すときは .dispatch
を使う.そして actions の中で Promise を実装しているので,ここでは .then
で受けて,ローディング画像を消している.
// shopping-cart/src/components/ProductList.vue created () { this.loading = true store.dispatch('fetchProducts') .then(() => this.loading = false) }
(実装画面)
Store Access from all Components
全てのコンポーネントで store を import しなくても良いように,ルートコンポーネントに注入し,各コンポーネントでは this.$store
で参照できるようにした.
// shopping-cart/src/main.js new Vue({ el: '#app', store, render: h => h(App) })
Add products to the cart
次に商品をカートに入れられるようにストアにカートを実装した.ここまでの理解もあったので actions と mutations の実装も問題なかった.
(実装画面)
Vuex Mutation History and Vue Devtools
Chrome 拡張を使って Vuex の状態を確認した.状態を Time Travel して戻すことができるのは,デバッグするときに便利そうだった.
(実装画面)
Cart items and total
Vuex の GitHub から Filters 用の currency.js
をコピーして,カートの実装を追加した.これで,カートの中に何が何個入っていて,総額も確認できるようになった.
(実装画面)
Checkout
次は,購入できるようにチェックアウトボタンを実装した.実際にチェックアウトはできないので,ボイラープレートに入っている実装で,ランダムで成功したり,失敗したりするようになっている.
buyProducts (products, cb, errorCb) { setTimeout(() => { // simulate random checkout failure. (Math.random() > 0.5 || navigator.userAgent.indexOf('PhantomJS') > -1) ? cb() : errorCb() }, 100) }
actions から,成功時の mutations と 失敗時の mutations を送っている.
// shopping-cart/src/store/index.js checkout ({state, commit}) { shop.buyProducts( state.cart, () => { commit('emptyCart') commit('setCheckoutStatus', 'success') }, () => { commit('setCheckoutStatus', 'fail') } ) }
Dynamic Vuex Getters
在庫がないときに商品名が消えてしまうのは微妙なので,Add to cart ボタンを disable にする実装をした.
(実装画面)
Vuex Map Helpers
ここからは,Vuex のお作法でリファクタリングをしていく.まずは Map Helpers で state / getters / actions
の宣言をシンプルにするために mapState / mapGetters / mapActions
を宣言し,スプレッド演算子とともに,その中に呼び出し名を宣言できる.
// shopping-cart/src/components/ProductList.vue computed: { ...mapState({ products: state => state.products }), ...mapGetters({ productIsInStock: 'productIsInStock' }) }
Split Vuex Store in Multiple Files
次は store/index.js
の肥大化を防ぐために,actions だけを store/actions.js
に切り出した.ただし,ここでは切り出せることを学ぶだけで,実際には次に出てくる Vuex Modules で書き直すことになる.
Vuex Modules
モジュールも Vuex のコンポーネントで,ストアごとに切り出していくようなイメージになる.今回は store/modules/cart.js
と store/modules/products.js
に切り出した.モジュールごとに state / getters / mutations / actions
を持っているので,わかりやすいし,テストコードも書きやすそう.結果的に store/index.js
に実装はなくなり,とにかく薄くなった.
// store/index.js import Vuex from 'vuex' import Vue from 'vue' import actions from './actions' import cart from './modules/cart' import products from './modules/products' Vue.use(Vuex) export default new Vuex.Store({ modules: { cart, products }, state: { // = data }, getters: { // = computed properties }, actions, mutations: { } })
Namespaced Vuex Modules
最後は namespaced で,モジュールで namespaced: true
を指定することにより,コンポーネント側で mapActions
に名前空間を指定できるようになる.メソッド名にストア名を含めて冗長になっている場合など,シンプルに書けるようになる.
まとめ
- Vue School の無料コース「Vuex for Everyone」を受講した
- ショッピングカートを実装しながら,Vuex の基礎を学べた
- 動画だと理解できなかった部分などは公式ドキュメント(日本語)を参考にした
写経した GitHub リポジトリ
他にもある Vue School の無料コース
過去に受講した Firebase Realtime Database の無料コースも,Firebase Authentication の無料コースも良かった.