はじめに
2016年もあっという間に終わってしまいましたね.
2017年もどういうライブラリが流行っていくのか楽しみにしております.
2017年しょっぱなからVue.jsとLaravelを使ってサイト構築したので,備忘録的に記録したいと思います.
この記事でわかること
- Laravelのインストール方法
- Vuexを使った基本的な実装方法
参考
Laravel,Vue.js,Vuexと共に日本語ドキュメントが充実しているので,基本的なことはこちらを参考にしました..
作成するアプリの仕様
- API部分
-
/api/vuex
にリクエストがくると5秒後に["I'm glad that you waited"]
をレスポンスする.
-
- WEB部分
-
/vuex
にアクセスすると/api/vuex
にリクエストを送信する -
/api/vuex
からレスポンスを受け取り,レスポンス結果を表示する.
-
それでは,Laravelをインストールするところから順に行っていきます.
#実装手順
Laravelのインストール
まずはじめに,Laravelのインストールを行います.
$ brew install composer
$ composer global require laravel/installer
$ echo 'export PATH=$HOME/.composer/vendor/bin:$PATH' >> ~/.zshrc #適宜置き換えてください
$ source ~/.zshrc
$ laravel # 動いたらok
Laravel Installer 1.3.3
....
初期プロジェクト作成
$ laravel new sample
$ cd sample
$ php artisan serve # localhost:8000にアクセスでWelcomeページが見れるはず
package.jsonを見ると標準でVue.jsが同梱されています.
Vuexは,同梱されていないので,Vuexをインストールします.
$ npm install
$ npm install vuex --save-dev
API部分の作成
ルータの設定
api/vuex
にアクセスした場合に,レスポンスを返してほしいので,routes/api.php
に以下を記述します.
また,/vuex
にアクセスした場合に,リクエストを送信するページに遷移するように設定します.
routes/api.php
にrouteを設定した場合,自動的にapi
がprefixされます.
Route::get('/sample', 'SampleAPIController@index');
Route::get('/sample', 'SampleController@index');
コントローラの作成
コントローラを作成します.
$ php artisan make:controller SampleAPIController
$ php artisan make:controller SampleController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SampleAPIController extends Controller
{
//
public function index(){
return ["I'm glad that you waited"];
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SampleController extends Controller
{
//
public function index(){
return view('sample');
}
}
ビューの作成
Laravelの場合,make:auth
コマンドを打つと認証系のテンプレートを作成してくれます.今回は認証系の処理は使わないので,認証系の部分のみ削除しそれを利用したいと思います.
$ php artisan make:auth
あとは,作成されたファイルを以下のように書き換えたり,作成します.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="/css/app.css" rel="stylesheet">
<!-- Scripts -->
<script>
window.Laravel = <?php echo json_encode([
'csrfToken' => csrf_token(),
]); ?>
</script>
</head>
<body>
<div id="app">
@yield('content')
</div>
<!-- Scripts -->
<script src="/js/app.js"></script>
</body>
</html>
@extends('layouts.app')
@section('content')
<div class="container">
<p>Hello World</p>
</div>
@endsection
WEB部分の作成
VuexのインストールとVue.jsコンポーネントの実装
Laravelのプロジェクトは初めからVue.jsを同梱しています.
また,app.jsを見るとVue.jsがid=appの部分を対象にマウントしていることがわかります(el:の部分にマウントしたい場所の属性を書く).
Vue.jsのコンポーネントは,マウントした属性のタグ内で適用することができます.
Vue.component('example', require('./components/Example.vue'));
const app = new Vue({
el: '#app'
});
また,上記のresources/views/layouts/app.blade.php
内にはすでにapp
属性となる部分が存在しています.
つまり,app.blade.php
を継承したView内では,Vue.jsのコンポーネントを使用できるようになっているということです.
app.bale.php
を継承したViewを作成します(参考:Laravel 5.3 Bladeテンプレート).
@extends('layouts.app')
@section('content')
<div class="container">
<p>Hello World</p>
<example></example>
</div>
@endsection
Vue.jsコンポーネントを使用できるか確認するために,上記のように<example></example>
を書き加えてみてください.ExampleComponent
の中身が表示されるはずです.
なお,ExampleComponentの実体は,resources/assets/js/components/Example.vue
にあります.
Vuexの実装
今回は,1つのカスタムコンポーネント(Vue.js)に対し,1つのModuleファイル(Vuex)を作成します.
手順としては,以下のようになります.
- Moduleの作成
- stateの作成
- gettersの作成
- actionsの作成
- mutaitonsの作成
- カスタムコンポーネントの作成
- Moduleの作成
Moduleの作成
APIにリクエストを送信し,レスポンスを受け取るという動作を挟むためにAjax通信を利用します.
そこで,公式で推奨されているXHRライブラリのaxios
を用います.
$ npm install axios --save-dev
まず,axiosを使った非同期処理プログラムを書きます.
import axios from 'axios';
export default{
sendRequest(url, callback, errorCallBack){
axios.get(url)
.then(response => {
callback(response.data)
})
.catch(error => {
errorCallBack(error);
})
},
}
次に,componentを書きます.
mutaiton-types
を作り,ACTION名等の共通化を安易にしています.これによって,IDEからATCTION名等を扱いやすくなります.
export const TOGGLE_STATE = 'TOGGLE_STATE';
export const SEND_REQUEST = 'SEND_REQUEST';
export const CHANGE_CONTENTS = 'CHANGE_CONTENTS';
import * as types from '../mutation-types'
import api from '../../api/sample'
const state = {
receive_response: false,
response_contents: 'Please wait Response...',
};
// getters
const getters = {
receiveResponse: state => state.receive_response,
responseContents: state => state.response_contents,
};
// actions
const actions = {
[types.TOGGLE_STATE] ({commit}, target){
commit(types.TOGGLE_STATE,target);
},
[types.SEND_REQUEST] ({commit}, url){
console.log("SEND REQUEST:" + url);
api.sendRequest(url,
response => {
console.log("CATCH RESPONSE:" + url);
commit(types.CHANGE_CONTENTS, {target:'response_contents', contents:response[0]})
commit(types.TOGGLE_STATE, 'receive_response')
},
error => {
console.log(error)
}
)
}
};
// mutations
const mutations = {
[types.TOGGLE_STATE] (state, target){
state[target] = !state[target];
},
[types.CHANGE_CONTENTS] (state, {target, contents}){
state[target] = contents;
}
};
export default {
state,
getters,
actions,
mutations
}
カスタムコンポーネントの作成
次に実際にカスタムコンポーネントを作成します.
computedが何か,mountedが何か等はドキュメントを見てください.
<template>
<div>
<div>HelloWorld</div>
{{ this.responseContents }}
<div v-if="this.receiveResponse"> Thank you! </div>
</div>
</template>
<script>
import {TOGGLE_STATE, SEND_REQUEST} from '../store/mutation-types';
export default{
mounted: function() {
this.$store.dispatch(SEND_REQUEST, '/api/sample')
},
computed: {
receiveResponse(){
return this.$store.getters.receiveResponse
},
responseContents(){
return this.$store.getters.responseContents
}
}
}
</script>
Moduleの登録
つぎに,作成したカスタムコンポーネント,Moduleの登録を行います.
import Vue from 'vue'
import Vuex from 'vuex'
import sample from './modules/sample.js'
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
sample
},
})
import store from './store';
Vue.component('example', require('./components/Example.vue'));
Vue.component('sample', require('./components/Sample.vue'));
const app = new Vue({
el: '#app',
store
});
これで,登録完了です.
あとは,<sample></sample>
と書けば今回の実装は完了です.
ここまでのコードは,githubを見て下さい.
リファクタリング
mapState
,mapAciton
を使うことでコード量を削減することができます.
また,Laravel CollectiveのRoute Annotaitonを使うことでRoutesの記述量を削減することができます.
mapState
,mapAction
を適用した例を以下に示します.
CustomComponentの場合,namespace
をtrue
にしないとmapState
を使えないかもしれません.(詳しく確認してないのですが,私の環境では動きませんでした.)
<template>
<div>
<div>HelloWorld</div>
{{ response_contents }}
<div v-if="receive_response"> Thank you! </div>
</div>
</template>
<script>
import {mapState, mapActions} from 'vuex';
import {TOGGLE_STATE, SEND_REQUEST} from '../store/mutation-types';
export default{
mounted: function() {
this.SEND_REQUEST('/api/sample');
console.log(this.receive_response);
},
computed: {
...mapState('sample', [
'receive_response',
'response_contents',
]),
},
methods: {
...mapActions('sample', [
TOGGLE_STATE,
SEND_REQUEST,
])
}
}
</script>
import * as types from '../mutation-types'
import api from '../../api/sample'
const namespaced = true;
const state = {
receive_response: false,
response_contents: 'Please wait Response...',
};
// actions
const actions = {
[types.TOGGLE_STATE] ({commit}, target){
commit(types.TOGGLE_STATE,target);
},
[types.SEND_REQUEST] ({commit}, url){
console.log("SEND REQUEST:" + url);
api.sendRequest(url,
response => {
console.log("CATCH RESPONSE:" + url);
commit(types.CHANGE_CONTENTS, {target:'response_contents', contents:response[0]})
commit(types.TOGGLE_STATE, 'receive_response')
},
error => {
console.log(error)
}
)
}
};
// mutations
const mutations = {
[types.TOGGLE_STATE] (state, target){
state[target] = !state[target];
},
[types.CHANGE_CONTENTS] (state, {target, contents}){
state[target] = contents;
}
};
export default {
namespaced,
state,
actions,
mutations
}