cheezBlog

nuxtにおけるvuexのしくみまとめ

vuexとは?

vuexとは、公式曰く「状態管理パターン + ライブラリ」です。 共有したい状態を管理したい時に一括で状態管理ができるため、コンポーネント間のデータのやりとりを簡易化します。

公式ドキュメント

state

  • stateは、ストアオブジェクトの状態を保持することができるプロパティ。

mapStateヘルパー

  • mapStatestateを一括で便利に扱えるようになるもの。

Vuex: mapStateの使い方を理解する

// before
computed: {
  hoge() {
    return this.$store.state.hoge;
  },
  fuga() {
    return this.$store.state.fuga;
  },
},

// after
import { mapState } from 'vuex'

export default {
  computed: ...mapState({   
    hoge, fuga
  })
}

getter

  • getterは、ストア内で定義できるゲッター。いわゆる、ゲッター。
  • computedと同じように、依存関係の一部が変更されたときにのみ再計算される
  • アロー関数と相性が良い。(returnが省略できるから。)
  • Vuex で modules を利用した際の getters の使い方が参考になりそう。

mapGettersヘルパー

  • ストアのゲッターを一括で使えるようにできるやつ
  • 使い方はstateと一緒。

mutation

  • mutationstateの内容を変えれる場所。セッター的なやつ...?
  • stateの変更は必ずmutationで行う。(データの変更を予測・追跡しやすくするため)
  • 第一引数はstate。第二引数以降は自由に設定可能。
  • 直接ミューテーションハンドラを呼び出すことはできず、store.commit('ミューテーション名', 第二引数以降)でイベントを登録するような感じで呼び出すことができる。
  • mutationは非同期的な処理がかけない...! 常に同期的。
    • 状態の変更が同期なのか非同期なのか、予測ができなくなるのを回避するため。
    • 非同期の処理ではactionを使う。

mapMutationsヘルパー

  • ストアのミューテーションを一括で使えるようにするやつ
  • 使い方はstateと一緒。

action

  • アクションは、状態を変更するのではなく、ミューテーションをコミットするもの。
  • 非同期処理を含むことができるのが最大の特徴。
  • 第一引数はcontext(分割代入のショートハンドで使いたいやつだけ取ってくることも可能)。第二引数以降は自由に設定可能。
    • contextには、commitstatedispatchなどをとることができる。
  • アクションはstore.dispatchがトリガーとなって実行される。
  • 直接アクションを呼び出すことはできず、store.dispatch('アクション名', 第二引数以降)でイベントを登録するような感じで呼び出すことができる。

mapActionsヘルパー

  • ストアのアクションを一括で使えるようにするやつ
  • 使い方はstateと一緒。

nuxtServerInitアクション

  • アプリケーションがロードされたとき、サーバーサイドで実行される。
  • サーバーサイドからクライアントサイドに直接渡したいデータがあるときに使う。
  • ルートモジュール(store/index.js)でしか使えない。

実際のコードとその説明

cheezBlogにおける、全タグの管理の流れを説明してみる。

  • store/index.js
// storeディレクトリ内のすべての *.js ファイルが名前空間付きモジュールに変換される
// index.jsはルートモジュールとして存在する

// contentfulのAPIから非同期でタグのデータを取得するためのプラグインをインポート
import createClient from '@/plugins/contentful.js';
const client = createClient;

/*
* state
*/

// 一旦空の状態で準備。
export const state = () => ({
  tags: [{ id: '', name: '' }],
});

/*
* mutations
*/
export const mutations = {
  // state.tagsの中身を書き換えることができる。
  // 今回はAPIを叩いてるため、同期処理しかできないmutationをcommitするだけじゃ使えない。
  SET_TAGS(state, tags) {
    state.tags = tags;
  },
};

/*
* actions
*/
export const actions = {
  // アプリケーションがロードされたときに、サーバーサイド側で処理
  async nuxtServerInit({ dispatch }) {
    // actionを発火させる
    await dispatch('getTags');
  },

  // getTagsはclient.getEntries(config)が取得できた際に実行。
  async getTags({ commit }) {
    const config = {
      content_type: 'articleTag',
      order: '-sys.createdAt',
    };
    const { items } = await client.getEntries(config);
    const tags = items.map(({ fields, sys }) => ({
      id: sys.id,
      name: fields.name,
    }));

    // mutationを実行
    commit('SET_TAGS', tags);
  },
};
  • TagsList.vue
<template>
 ...
</template>
<script>
import { mapState } from 'vuex';

export default {
  computed: {
    // $store.state.tagsを取得
    ...mapState(['tags']),
  },
};
</script>