LoginSignup
90
83

More than 3 years have passed since last update.

Vue.jsでVuex+axiosを利用してAPIを叩く

Last updated at Posted at 2018-09-12

概要

Vue.jsでVuex+axiosを利用してみました。
コードをシンプルにすると理解が進んで良いです^^

GitHubにコードをUPしましたので、よろしければご参考ください。
https://github.com/kai-kou/vue-js-typescript-vuex-axios

環境構築

Dockerを利用して、APIのモックサービスも立ち上げられるようにdrakovを利用しています。

drakovについては下記をご参考ください。

api blueprintとdrakovを利用してAPIモックサーバを立ち上げる
https://qiita.com/kai_kou/items/bdbb6c3f8d1ac655595d

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> vi Dockerfile
> vi docker-compose.yml
Dockerfile
FROM node:10.8.0-stretch

RUN npm install --global @vue/cli
RUN npm install -g drakov

WORKDIR /projects
docker-compose.yml
version: '3'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - ".:/projects"
    tty: true
  drakov:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - "./docs:/projects"
    tty: true
    command: drakov -f "**/*.md" --public --watch --p 3000

API用のmdファイルを用意しておきます。

> mkdir docs
> touch docs/sample.md
docs/sample.md
# GET /
+ Response 200 (text/plain)

        Hello World!

コンテナを起動して、Vue.jsのプロジェクトを作成するコンテナに入ります。

> docker-compose up -d
> docker-compose exec app bash
コンテナ内
> vue create app

Vue CLI v3.0.1
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Vuex, Linter, Unit
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript for auto-detected polyfills? Yes
? Pick a linter / formatter config: TSLint
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Mocha
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn
  Use NPM

プロジェクトが作成されたらVue.jsサービスを起動します。

コンテナ内
> cd app
> yarn serve

src/store.tsに実装を追加します。
Stateにcountermessage を用意してcounter をインクリメントするアクションと、APIからテキストを取得してmessage に保存するアクションを追加しました。

src/store.ts
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';

Vue.use(Vuex);

interface State {
  counter: number;
  message: string;
}

export default new Vuex.Store({
  state: {
    counter: 0,
    message: '',
  } as State,
  getters: {
    getCounter: (state, getters) => () => {
      return state.counter;
    },
    getMessage: (state, getters) => () => {
      return state.message;
    },
  },
  mutations: {
    increment(state, payload) {
      state.counter+= 1;
    },
    setMessage(state, payload) {
      state.message = payload.message;
    },
  },
  actions: {
    incrementAction(context) {
      context.commit('increment');
    },
    async getMessageAction(context) {
      const msg = await axios.get('http://localhost:3000').then(
         (res) => res.data,
         () => ''
      ); 
      const payload = {
        message: msg,
      };
      context.commit('setMessage', payload);
    },
  },
});

src/main.tsはそのままです。

src/main.ts
import Vue from 'vue';
import App from './App.vue';
import store from './store';

Vue.config.productionTip = false;

new Vue({
  store,
  render: (h) => h(App),
}).$mount('#app');

src/App.vueでStoreが利用できるようにします。
画像をクリックとcounter のインクリメントとAPIアクセスがされるようにしています。

src/App.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
    <img alt="Vue logo" src="./assets/logo.png" @click="increment">
    <HelloWorld :msg="`Welcome to Your Vue.js + TypeScript App ${this.counter}`"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from './components/HelloWorld.vue';

@Component({
  components: {
    HelloWorld,
  },
})
export default class App extends Vue {
  private get counter(): number {
    return this.$store.getters.getCounter();
  }
  private get message(): string {
    return this.$store.getters.getMessage();
  }

  private increment(): void {
    this.$store.dispatch('incrementAction');
    this.$store.dispatch('getMessageAction');
  }
}
</script>
(略)

ブラウザで確認してみます。

> open http://localhost:8080/

スクリーンショット 2018-09-04 14.25.28.png

はい。
うまくAPIにアクセスできてますね。

ポイントとしてはsrc/store.tsでAPIアクセス時にasyncawait を利用するところでしょうか?利用しなくても以下のように書けますが、フラット化を意識していないと、行き着く先に、きっとコールバック地獄が待っていると思います^^

src/store.ts(抜粋)
    getMessageAction(context) {
      axios.get('http://localhost:3000').then(
        (res) => res.data,
        () => ''
      ).then(msg => {
        const payload = {
          message: msg,
        };
        context.commit('setMessage', payload);
      });
    },

追記(2020/04/06)

ネストを回避する手段について、@masmatsum さんからコメントを頂きました。

コメントにあるサンプルコードや記事のように、ネストを回避する手段はasyncawait を利用する他にもありますので、ケースに合わせた実装方法をご検討ください。


GitHubの方に、vuex-type-helperというライブラリを利用してstoreをモジュール化する実装も置いてますので、ご参考ください。

vuex-type-helperについては別記事にまとめています。

VuexをTypeScriptで利用するのに悩んだ
https://qiita.com/kai_kou/items/fdd8ecd07995571da8a1

それでは、良きVuex+axiosを利用したVue.js開発ライフを^^

Vue.js+TypeScriptで開発するときの参考記事まとめ
https://qiita.com/kai_kou/items/19b494a41023d84bacc7

90
83
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
90
83