寝て起きて寝て

プログラミングが出来ない情報系のブログ

Vuexメモ

Vuexとは

公式

コンポーネント間でデータのやり取りをすると、親子のネストが深いと
バケツリレーのようにデータの受け渡しが発生し作りが煩雑な作りになる。
Vuexはそれを防ぐために使われるモジュールのこと。
公式には状態管理パターン + ライブラリと呼ばれている。
ここでの状態管理とは、コンポーネントで使用されるデータのことを言っている。

ストアはデータの入れ物でコンポーネントがストアにアクセスすることで、
データのやり取りをすることができる。
ストアは一つのアプリケーションにつき一つのストアを使用したほうがいい
これはデータの整合性を保ち管理しやすくするようにするため。

Vuexの機能は下記で構成されている。

  • Action
  • Mutation
  • State
  • Getter

Action

コンポーネントからデータの取得や、データの更新の支持を受ける機能
この機能ではコンポーネント側へデータを渡す事はできない。

Mutation

Actionから受け取ったデータをStateへ格納したり、
既存のデータを更新したりする。
すべてのデータ変更はこのMutationを介してStateへ格納される。
つまり Vuex のストアの状態を変更できる唯一の存在
SQLのinsertとupdate的な

State

データの保持をしている機能。
ストア内のデータはすべてここに入る。
信頼できる唯一の情報源
一応コンポーネントはここからデータを直接取り出せる。
データを加工しない場合、ここに保存されたデータを直接参照したい場合はここから取り出す。
DB的な

Getter

GetterはStateの情報を加工し、コンポーネントへ渡したい場合に使用する。
例)StateのUserListから特定のUserを取り出したい場合など
取り出し専用のSQL文的な

使い方

※ここに書かれているのはVueの新規プロジェクトの作成から立ち上げまで
で作成した後のファイル構成を前提にして書いています。

src/store/index.jsを編集する。初期状態は下記の通り。
Getterの記載がないが、必要なときはgetters:{},のようにして使用する。

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

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

値の受け渡しをしたい変数をstateに記載

  state: {
      drawer:false
  },

actionsには下記のように記載

  actions: {
    toggleSideMenu({ commit }){
      commit('toggleSideMenu')
    }
  },

これは何をやっているかというとActionで定義されたメソッドの引数では、
自動的にcontextオブジェクトが入ってくる。引数の{{commit}}は、
context.commitメソッドだけを受け取るようにした書き方。
受け取れるメソッドはここ

ちなみに引数がある場合は、
toggleSideMenu({ commit }, input1){
のように書く。

commitメソッドはmutationのメソッドを呼び出すために使用されるデータのこと、
ここではtoggleSideMenuという文字列を引数として渡しているので、
この文字列に対応したdrawer変数を変更する処理をmutationsに記載する。

mutations: {
    toggleSideMenu(state){
      state.drawer = !state.drawer
    }
  },

これでcommittoggleSideMenuが呼び出された場合
mutationstoggleSideMenuが呼び出される。
mutationsのメソッドの引数には自動的にstateオブジェクトが入ってくるので、
stateで宣言したdrawerの値を変更することができる。

ちなみに引数がある場合は、
toggleSideMenu(state, input1){
のように書く。

コンポーネントからストアを呼び出す方法

下記はsrc/components/SideNav.vueに記載する

ストアへのアクセスの仕方は$storeでアクセスできて、
stateを使用する場合は$store.stateと記載する。
そして今回はdrawerにアクセスしたいので、$store.state.drawerのように記載する

これで開閉状態をストア依存でできるようになった。
完成形は下記

<template>
  <v-layout wrap style="height: 200px;">
    <v-navigation-drawer v-model="$store.state.drawer" absolute temporary>
      <v-list class="pa-1">
        <v-list-tile avatar>
          <v-list-tile-avatar>
            <img src="https://avatars2.githubusercontent.com/u/1363954?s=460&v=4">
          </v-list-tile-avatar>

          <v-list-tile-content>
            <v-list-tile-title>Kazuya Kojima</v-list-tile-title>
          </v-list-tile-content>
        </v-list-tile>
      </v-list>

      <v-list class="pt-0" dense>
        <v-divider></v-divider>

        <v-list-tile v-for="item in items" :key="item.title" :to="item.link">
          <v-list-tile-action>
            <v-icon>{{ item.icon }}</v-icon>
          </v-list-tile-action>

          <v-list-tile-content>
            <v-list-tile-title>{{ item.title }}</v-list-tile-title>
          </v-list-tile-content>
        </v-list-tile>
      </v-list>
    </v-navigation-drawer>
  </v-layout>
</template>

<script>
export default {
  data () {
    return {
      items: [
        { title: '連絡先一覧', icon: 'list' }
      ]
    }
  }
}
</script>

次はヘッダーのアイコンからdrowerの値を変更できるようにする。
対象のファイルはsrc/App.vue

弄るのはここ<v-toolbar-side-icon></v-toolbar-side-icon>
ここをクリックされた時にActionが呼び出され、drowerの値が変更されるように処理を追加する。
(メニューバーの開閉ができるようにする) Actionの呼び出し方には二種類ある。

Actionの呼び出し方-dispatch

data部分を下記のように変更する。

export default {
  name: 'App',
  components:{
    SideNav
  },
  data () {
    return {
      //
    }
  },
  methods:{
    openSideMenu(){
      this.$store.dispatch('toggleSideMenu')
    }
  }
}

methodsstoreを呼び出すときはthis.$storeで呼び出すことができる 。
そしてstoredispatchというメソッドを持っていて、このメソッド経由でActionを呼び出すことができる。
今回はsrc/store/index.jsで記載したtoggleSideMenuのアクションを呼び出せる。

後はこのopenSideMenuメソッドを@clickで呼び出せば使える
<v-toolbar-side-icon @click.stop="openSideMenu"></v-toolbar-side-icon>

App.vueの最終形

<template>
  <v-app>
    <v-toolbar app>

    <v-toolbar-side-icon @click.stop="openSideMenu"></v-toolbar-side-icon>
      <v-toolbar-title class="headline text-uppercase">
        <span>マイアドレス帳</span>
      </v-toolbar-title>
      <v-spacer></v-spacer>
    </v-toolbar>
    <SideNav/>>
    <v-content>
    </v-content>
  </v-app>
</template>

<script>
import SideNav from './components/SideNav'
export default {
  name: 'App',
  components:{
    SideNav
  },
  data () {
    return {
      //
    }
  },
  methods:{
    openSideMenu(){
      this.$store.dispatch('toggleSideMenu')
    }
  }
}
</script>

Actionの呼び出し方-mapActions

mapActionsを使うとdispatchを使うよりも簡単に記述することができる

先程dispatchでも書き換えたsrc/App.vuemapActions用に変更する
mapActionsメソッドを使うにはimport { mapActions } from 'vuex'でインポートする必要がある。
これにより、コンポーネントのメソッドにActionメソッドを組み込む事ができる。
ちなみに普段のインポートとは違い{mapActions}のように記載しているのは、ES6の分割代入を利用している
分割代入をについてはここ

その後methodsを下記のように変更する。

  methods:{
    ...mapActions(['toggleSideMenu'])
  }

mapActionsメソッドの引数には配列を渡すして使用したいActionメソッド名を記載
複数使用したいActionがある場合は配列に追加するとメソッドに組み込める。
mapActionsの前の...は分割代入の構文

これにより、toggleSideMenuは不要になりtoggleSideMenuだけですむので、
こちらを使うほうが読みやすくなる。

App.vueの完成形は下記

<template>
  <v-app>
    <v-toolbar app>

    <v-toolbar-side-icon @click="toggleSideMenu"></v-toolbar-side-icon>
      <v-toolbar-title class="headline text-uppercase">
        <span>マイアドレス帳</span>
      </v-toolbar-title>
      <v-spacer></v-spacer>
    </v-toolbar>
    <SideNav/>>
    <v-content>
    </v-content>
  </v-app>
</template>

<script>
import SideNav from './components/SideNav'
import { mapActions } from 'vuex'
export default {
  name: 'App',
  components:{
    SideNav
  },
  data () {
    return {
      //
    }
  },
  methods:{
    ...mapActions(['toggleSideMenu'])
  }
}
</script>

Getterの呼び出し方-mapGetters

使い方はmapActionsと大体同じだけど、呼び出すときはmethodsではなく
computedで定義する

まずvue/state/index.jsを下記のように書く

getters:{
    userName: state => state.login_user ? state.login_user.displayName : '',
    photoURL: state => state.login_user ? state.login_user.photoURL:''
  },

コンポーネント側では下記のようにimportし配列形式で定義
あとはテンプレート側から呼び出したりできる。

import { mapGetters } from 'vuex'

~~~~~~~~~~~~~~~

computed:{
    ...mapGetters(['userName','photoURL'])
  }