もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

Vue.js入門 基礎から実践アプリケーション開発までを読んだ vol.7

はじめに

こんにちは。

Kojirockの1人アドベントカレンダー Advent Calendar 2018の7日目の記事です。

以下の本を読んで勉強できたことを記載していきます。

今回は第7章です。

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

Vuexによるデータフローの設計・状態管理

目次

7.1 複雑な状態管理

7.2 データフローの設計

7.3 Vuexによる状態管理

7.4 Vuexのコンセプト

7.5 タスク管理アプリケーションの状態管理

7.6 ストアのモジュール分割

7.7 VuexストアとVueコンポーネント間の通信

7.8 VuexとVue Routerの連携

7.1 複雑な状態管理

このブロックでは、タスク管理のアプリケーションを例にとって、Vuexを使用せず、データフローのことを考えない場合、どのような辛みがあるのかを完結に説明されています。

7.2 データフローの設計

単方向データフロー・双方向データフローについてが説明されています。

このあたりの説明は、公式サイトのほうが簡素かつわかりやすく纏まっていたような気がします。

https://vuex.vuejs.org/ja/

単方向データフローにするメリットとして、本書で紹介されていた以下の文もイメージしやすかったです。

データを取得しつつ更新するといったようなことができなくなり、実装やデバッグが単純になる。 データを取得、更新するために何をするかの選択肢が絞られて、理解が容易なコードを書きやすい。

7.3 Vuexによる状態管理

vuexを使用するメリットなどが述べられています。

実際のインストール方法も乗っています。

今回は、前回の記事で使用した Vue CLIでVuexをすでにインストール済みでしたので、飛ばします。

Vue CLIでインストールしたVuexのファイルの中身は以下です。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {

  },
  mutations: {

  },
  actions: {

  }
})

7.4 Vuexのコンセプト

表題の通りVuexのコンセプトについて述べられています。

以下の一文が気になりました。

Vuexはライブラリとしての機能だけではなく、実際のパターンも含めてVuexであるという見方をすべきです。

ストア

アプリケーションの状態を保持する役割を持っています。

上記に記載しているスクリプトexport default new Vuex.Store({ となっていますね。

ストアの構成要素は以下の4つということです。

  • State

    => アプリケーションのステート(状態)

  • Getter

    => 状態の一部や、状態から計算された値を返すゲッター

  • Mutation

    => 状態を更新できる

  • Action

    => 外部APIとのやり取りを行う

基本的にストアはアプリケーションに1つ持つということですが、大規模なプリケーションの場合はストアを モジュール という単位で分割して管理することもできるようです。

ステート

前述したとおり、アプリケーションの状態をオブジェクトで持っています。

Vuexにアプリケーションの全ての状態をもつという設計も可能ですが、それはやりすぎです。

あるコンポーネントでしか使わないようなデータは、ストアを使わず data に持たせたほうが、よいです。

Vuexに管理させたい場合の判断材料としては、「アプリケーション全体に関わるデータ」はストアに持たせるというのが、推奨されている使い方のようです。

ストアに持たせたほうがよい

  • ログイン情報
  • 共通のheaderなどでバッジで表示する、アプリケーションからのNotification通知

コンポーネントのdata

ゲッター

ストア版computed(算出プロパティ)という感じのようです。

通常の算出プロパティ同様、キャッシュが可能のようです。

getters: {
    aaa: (state) => state.sum * state.sum,
    
    bbb: (state, getters) => state.sum * getters.aaa
}

ミューテーション

ステートを更新するために使用されます。

ReduxでいうReducerっぽい感じかと思いますが、Reducderと違うのはstoreが編集可能というところでしょうか?

Reducerは既存の状態と、今回やりたい変更をマージして、新しい状態を作るという感じなので、読み取り専用だったような気がしてます。

いいか悪いかは別として、個人的にはReduxよりわかりやすくはあります。

ミューテーションは、直接呼び出すことはできません。

store.commit('ミューテンション名') という感じで呼び出します。

store.commit実行時に、オブジェクトで第二引数を渡すと、ミューテーションの第二引数 payloadとして渡すことができます。

mutations: {
    mu_01: (state, payload) {
        state.amount = state.amount + payload.amount
    }
}


store.commit('mu_01', { amount: 10 })

そこまで難しい感じではないですね。

注意点としては、ミューテーション内で非同期通信処理を含めないほうがよいということです。

非同期通信処理を行いたい場合は、次のアクションで行うべきです。

アクション

非同期処理や外部のAPIとの連携を行い、最終的にステートを更新するためのミューテーションを呼び出すために使用します。

アクションもミューテーションと同じく、直接呼び出せません。

store.dispatch('アクション名') という形で呼び出します。

actionのメソッドの引数は1つで、コンテキストという引数が渡されてきます。

  • state: 現在のステート
  • getters: 定義されているゲッター
  • dispatch: 他のアクションを実行するメソッド
  • commit: ミューテーションを実行するメソッド

このブロックの最後に、アクションでajaxを使用してデータをとってくるような擬似的コードが記載されています。

目新しいところはありませんでしたが、 非同期通信のコールバック時に、引数のコンテキストのcommitを使用して、ミューテーションを実行して、ストアを変更しています。

7.5 タスク管理アプリケーションの状態管理

こちらは例題として、簡単なタスク管理アプリケーションを開発して、Vuexの使用方法を学びます。

こちらは写経しました。

シンプルな機能だったので、「そうなってしまった」のかもしれませんが、storeに処理を委譲しているようなロジックでした。

なるほどと思ったのは、算出プロパティの追加方でした。

なんとなく計算結果を返すというイメージだったのですが、storeの情報を返すようにするという使い方は、コードも読みやすいし、わかりやすかったです。

  ## component

  computed: {
    tasks () {
      return this.$store.getters.filteredTasks
    },

    labels () {
      return this.$store.state.labels
    },

    filter () {
      return this.$store.state.filter
    }
  },
  
  ## store
  
  state: {
    tasks: [
      {
        id: 1,
        name: '牛乳を買う',
        labelIds: [1, 2],
        done: false
      },
      {
        id: 2,
        name: 'Vue.jsの本を買う',
        labelIds: [1, 3],
        done: true
      }
    ],

    labels: [
      {
        id: 1,
        text: '買い物'
      },
      {
        id: 2,
        text: '食料'
      },
      {
        id: 3,
        text: '本'
      }
    ],

7.6 ストアのモジュール分割

先程から出てきているストアの分割の話です。

モジュールという単位で分割できます。

const AAA = {
    state: {
    },
    
    getters: {
    },
    
    mutations: {
    },
    
    actions: {
    },
    
    modules: {
      childModule: {
        // モジュールの入れ子もできる
      }
    }
}

const store = new Vuex.Store({
    modules: {
        AAA
    }
})

7.7 VuexストアとVueコンポーネント間の通信

コンポーネントからストアへのアクセス方法が記載されています。

this.$store でアクセスすることができます。

また別の方法として、ヘルパー関数でのアクセスも紹介されています。

mapState, mapGetters, mapMutations, mapActionsです。

具体的な使用例が本書に記載されていました。

こちらも写経いたしました。

7.8 VuexとVue Routerの連携

vuex-router-syncでこちらの問題は解決します。

import { sync } from 'vuex-router-sync'

sync(store, router)

// syncしたとことでstore.state.route以下にルーティングのデータが入る。
console.log(store.state.route)

終わりに

大型の写経部分があったので、ガッツリコードを書きました。

また、今回は写経が多い章だったので、そのままガッツリコードを乗せるわけにも行かず、書くのに苦労しました。

ただ、ガッツリ写経できたおかげで基本的な動きはつかめたと思います。

写経したコードもとてもわかり易かったので、まだ未読の方はぜひ買ってみてください。

nuxtなどと連携した際また違った動きになるのかどうか、そのあたりももう少し勉強しようと思います。

おしまい。