WEBOPIXEL

Laravel + Vue.js にVuexを導入する

Posted: 2018.10.19 / Category: javascript, PHP / Tag: ,

前回はLaravel + Vue.js でJWT認証をやりましたが、状態管理があれだったので、今回はVuexを導入してもう少しちゃんとにやってみたいと思います。

Sponsored Link

使用環境
Laravel 5.7
jwt-auth 1.0.0-rc3
Vue 2.5
vue-router 3.0
vuex 3.0

この記事は下記の続きです。

Laravel + JWTAuth + Vue.js でAPIログイン認証の実装

ストアの作成

これがないと始まりません、npmでVuexをインストールします。

$ npm install vuex --save

最初にStoreファイルから作成していきましょう。
新たにstoreディレクトリを作成し、その中にindex.jsを作成します。

resources/js/store/index.js

import Vue from 'vue';
import Vuex from 'vuex';
import auth from "./modules/auth";
import alert from "./modules/alert";

Vue.use(Vuex);

export default new Vuex.Store({
	modules: {
		auth, alert
	}
});

今回は認証関係のステートを管理するauthと、「ログインしました。」みたいなメッセージを表示する用のalertストアを作成します。
後々拡張してく予定なので、modulesで分けておきましょう。

次にalertモジュールから見ていきましょう。

resources/js/store/modules/alert.js

const state = {
	message: '',    // 表示する文字列
	type: 'success' // クラス名に使用
};

const mutations = {
	setAlert (state, {message, type}) {
		state.message = message;
		state.type = type || 'success';
	}
};

export default {
	namespaced: true,
	state,
	mutations
};

アラートはサーバーとのやりとりはないのでstatemutationsだけです。

resources/js/store/modules/auth.js

import router from '../../router';

const state = {
	token: ''
};

const mutations = {
	login (state, payload) {
		state.token = payload;
	},
	logout (state) {
		state.token = null;
	}
};

const getters = {
	isLogin (state) {
		return state.token ? true : false;
	}
};

const actions = {
	login ({ commit }, payload) {
		axios.post('/api/login', {
			email: payload.email,
			password: payload.password
		}).then(res => {
			const token = res.data.access_token;
			axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;

			commit('login', token);
			router.push({path: '/'});
			commit('alert/setAlert', { 'message': 'ログインしました。' }, { root: true });
		}).catch(error => {
			commit('alert/setAlert', { 'message': 'ログインに失敗しました。', 'type': 'danger' }, { root: true });
		});
	},
	logout ({ commit }) {
		axios.post('/api/logout').then(res => {
			axios.defaults.headers.common['Authorization'] = '';
			commit('logout');
			router.push({path: '/'});
			commit('alert/setAlert', { 'message': 'ログアウトしました。' }, { root: true });
		}).catch(error => {
			commit('alert/setAlert', { 'message': 'ログアウトに失敗しました。' }, { root: true });
		});
	}
};

export default {
	namespaced: true,
	state,
	mutations,
	getters,
	actions
};

なんかすごいとてつもなくダメな気がしますが、とりあえず今の所こんな感じです。

ルーターの修正

ルーターはあまり変わらないですね。
store.getters['auth/isLogin']でログイン済みかの確認をしています。

resources/js/router.js

import Vue from 'vue';
import VueRouter from 'vue-router';
import store from './store/index';
import Home from './components/pages/Home';
import Login from './components/pages/Login';
import User from './components/pages/User';

Vue.use(VueRouter);

const routes = [
	{ path: '/', component: Home },
	{ path: '/login', component: Login },
	{ path: '/user', component: User, meta: { requiresAuth: true } }
];

const router = new VueRouter({
	mode: 'history',
	routes
});

router.beforeEach((to, from, next) => {
	// ページを切り替えたらアラート削除
	store.commit('alert/setAlert', { 'message': '' });
	if (to.matched.some(record => record.meta.requiresAuth)) {
		// ログインしていなかったらログインページにリダイレクト
		if (store.getters['auth/isLogin'] === false) {
			next({
				path: '/login',
				query: { redirect: to.fullPath }
			});
		} else {
			next();
		}
	} else {
		next();
	}
});

export default router;

コンポーネントの修正

最初にどのコンポーネントからでもストアをインポートしないで使えるように、app.jsを修正します。

resources/js/router.js

require('./bootstrap');
import Vue from 'vue';
import router from './router';
import store from './store/index';

Vue.component('app', require('./components/App.vue'));

const app = new Vue({
	store,
	router
}).$mount('#app');

new Vuestoreをインジェクトすることで、各コンポーネントからは、this.$storeでストアにアクセスできるようになります。

resources/js/router.js

<template>
	<div class="container">
		<div class="row justify-content-center">
			<div class="col-md-8">
				<div v-bind:class="'alert alert-' + alertType" v-if="alertMessage">{{ alertMessage }}</div>
				<ul>
					<li><router-link to="/">ホーム</router-link></li>
					<li><router-link to="/login">ログイン</router-link></li>
					<li><router-link to="/user">ユーザー情報</router-link></li>
					<li @click="logout">ログアウト</li>
				</ul>
				<hr>
				<router-view></router-view>
			</div>
		</div>
	</div>
</template>

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

	export default {
		computed: mapState('alert', {
			'alertMessage': 'message',
			'alertType': 'type'
		}),
		methods: mapActions('auth', [
			'logout'
		]),
	}
</script>

Vuexのmapヘルパーを使用すると、変数やメソッドを使用する感覚でストアとやりとりできるようになります。

resources/js/login.js

<template>
	<div>
		<form @submit.prevent="login">
			<h1>ログイン</h1>
			メールアドレス: <input type="email" v-model="email">
			パスワード: <input type="password" v-model="password">
			<button type="submit" class="btn btn-primary">ログイン</button>
		</form>
	</div>
</template>

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

	export default {
		data () {
			return {
				email: 'admin@example.com',
				password: '123456',
			}
		},
		methods: {
			login() {
				this.$store.dispatch('auth/login', {
					email: this.email,
					password: this.password
				});
			}
		}
	}
</script>

これでSPA的な認証周りができた感じなので、次回はからはメインとなる機能を作っていきたいと思います。

ここまでのソースコードはGithubに載せてあります。
LaravelTodoSPA