Vue.js/Nuxt.jsでVuexストアを使ってAPIで取得したデータを表示してみる

直接pageにaxios使ってデータを取得したら終わりじゃないの?と思ってたけど、コンポーネント間で共有できるようにVuexストアに保持したほうがいいのね!よくわからないけどw
ということで、ややこしそうだけどなるべくいろんな形に対応できるようにパターン化できたら簡単になるね。

pageでデータ取得してstoreへ

【APIで取得するサンプルデータ】

http://vue.localhost/pref_list.php

<?php
header('Access-Control-Allow-Origin:http://localhost:3000');
header('Content-Type:text/plain;charset=UTF-8');
$prefList = [
	1 => '北海道',
	2 => '青森県',
	3 => '岩手県',
	4 => '宮城県',
	5 => '秋田県',
	
];
echo json_encode($prefList);

※Access-Control-Allow-Originヘッダー
CORS(Cross-Origin Resource Sharing):クロスオリジン(ドメイン)リソース共有。
異なるドメインからのアクセスを許可することね。

【APIからサンプルデータを取得するヤツ】

axios使うよ

$ npm install axios

※npm v5から「–save」がデフォルトでつくので不要。

【APIアクセス】

※apiディレクトリを作って配置

api/pref.js

import axios from 'axios'

class PrefApi {
  constructor() {
    // ベースとなるURLを設定
    this.apiBase = 'http://vue.localhost'
  }

 // 一覧取得
  prefs() {
    // pref_list取得
    return axios.get(`${this.apiBase}/pref_list.php`)
    .then(json => {
      // 取得したデータはdataプロパティに配置されるね。
      return json.data;
    })
    .catch(e => ({ error: e }));
  }
}
// インスタンス化してオブジェクトをexport
const prefApi = new PrefApi();
export default prefApi;

【Vuexストア】

store/pref.js

import Vuex from 'vuex'

export const state = () => ({
  pref_list: []
})

export const mutations = {
    setList(state, payload) {
        state.pref_list = {...payload}
    },
};

mutationsの関数は第1引数にstate、第2引数に追加の引数(ペイロード)として主にデータを渡す。

※mutationsは状態を変更するもので同期処理のみに使用。
※actionsはミューテーションをコミットするもので非同期処理も可。

【ページ】

pages/list.vue

<template>
  <section>
    <div>
      <div v-for="(item, key, index) in $store.state.pref.pref_list" :key="index">
        <a class="button">{{ key }} - {{ item }}</a>
      </div>
    </div>
  </section>
</template>

<script>
  import prefApi from '@/api/pref'
  import {mapState} from 'vuex';

  export default {
    async fetch({store}) {
      let json = await prefApi.prefs();
      store.commit('pref/setList', json)
    },
    computed: mapState('pref', {
      pref_list: 'pref_list'
    })
  }
</script>

◆fetch({ context, params })

ページをレンダリングする前にストアのデータを設定するために使用するメソッド。

◆async/awaitについて

asyncは非同期関数を定義するという宣言で内部の関数はawaitをつけることでPromiseを返すらしい。
そうすることによりawaitの関数がresolved(解決)またはrejected(拒否)を返すまでpending(保留)として待つらしい。
jsonが取得できるまでcommitしないってことね!

◆mapStateについて補足

※「$store.state.pref.pref_list」はmapStateヘルパーを使用することで「pref_list」としてバインドできる。

  // mapStateヘルパーをインポート
  import {mapState} from 'vuex';

  export default
    ・・・
    computed: mapState('pref', {
      pref_list: 'pref_list'
    })

※同じ名前なら配列として渡せるので以下でも可。

computed: mapState('pref', ['pref_list']),

※mapStateだけでなく他の算出プロパティも設定するならオブジェクトスプレッド演算子「…」が必要らしい。

    computed: {
      ...mapState('pref', {
        pref_list: 'pref_list'
      }),
      x: function() {
        return 1
      }
    },

ストアでデータ取得する?

上述で『pageでデータを取得⇒ストアにデータを投げる』としたが
データをストアで取得することも考えてみた。

store/pref.js

import Vuex from 'vuex'
import prefApi from '@/api/pref'

export const state = () => ({
  pref_list: []
})

export const mutations = {
  setList (state, list) {
    state.pref_list = list
  }
}

export const actions = {
  async set_pref_list({commit}) {
    let list = await prefApi.prefs()
    commit("setList", list)
  }
}

※mutationsで非同期処理は書けないのでactionsでデータを取得。

pages/list.vue

<template>
  <section>
    <div>
      <div v-for="(item, key, index) in $store.state.pref.pref_list" :key="index">
        <a class="button">{{ key }} - {{ item }}</a>
      </div>
    </div>
  </section>
</template>

<script>
  import {mapState} from 'vuex';

  export default {
    created() {
      this.$store.dispatch('pref/set_pref_list')
    },
    computed: mapState('pref', {
      pref_list: 'pref_list'
    })
  }
</script>

いろんなやり方できて大混乱ですけど・・・
とりあえずこんな感じでどうでしょう♪