LaravelでSPAアプリケーションページを作成する

SPAとは

SinglePageApplicationの略で、画面遷移が発生せず、javaScriptなどを駆使して画面の内容を書き換えて動作するアプリケーションのことみたいです。
LaravelにはSPAを作成するための一式が組み込まれているようなので、簡単なSPAを作成してみたいと思います。

参考にしたのは、以下の記事になります。
<https://qiita.com/acro5piano/items/f33381fc60408abe9865>

Laravelプロジェクト作成

まずはLaravelプロジェクトを作成します。
いつものコマンドで作成

laravel new SPA

laradockの設定

開発環境のlaradockに先ほど作成したSPAプロジェクトを追加します。
nginxの設定を新規追加します

既存ファイルをコピーして作成
laradock\nginx\sites\spa.conf
中身の下記の部分を編集
server_name spa.test;
root /var/www/spa/public;
index index.php index.html index.htm;

以下のファイルを編集
C:\Windows\System32\drivers\etc\hosts
設定を追加
127.0.0.1 spa.test

laradockを起動し、とりあえずサイトにアクセスできるか確認します

docker-compose.exe up -d nginx mysql

サイトにアクセス
http://spa.test/

いつもの画面が出てくれば、とりあえずOK enter image description here

RestAPIの作成

コントローラの作成

artisanコマンドを利用してRest用コントローラを作成します。

php artisan make:controller SPAController --resource

コントローラを編集します。
とりあえず、メソッド名を返却する処理を追加します。

\SPA\app\Http\Controllers\SPAController.php

/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index()
{
    //
    return ['data' => 'index'];
}

ルートの設定

Rest用のルートを追加します。

\SPA\routes\api.php

Route::resource('spa', 'SPAController');

ここまで設定していかにアクセスすると、簡単なJsonが帰ってくることが確認できると思います。 http://spa.test/api/spa

SPAのベースページの作成

ルートの設定

/api/* 以外のURLにアクセスが来た時に応答するルートを設定する

\SPA\routes\web.php

Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');

これで、api以外にアクセスが来たときは、app.blade.phpが応答されるようになる。

クライアントサイドのルーティングライブラリ取得

vue-routerを使用するため、プロジェクトフォルダ内にあるpackage.jsonを編集

"vue-router": "^3.0.0"

npmコマンドでインストール

npm install

WebPack起動

npm run watch

が、起動したところ、いろいろエラーが出たので対処
エラー:Module build failed: Error: Cannot find module ‘babel-core’
対処:https://github.com/babel/gulp-babel/issues/124 の記事にあった、以下のコマンドを実行

npm install --save-dev babel-core babel-preset-env

エラー: Error: Can’t resolve ‘css-loader’, Module not found: Error: Can’t resolve ‘postcss-loader’
対処:https://github.com/webpack/webpack/issues/1470の記事の以下のコマンドを実施

npm install --save-dev css-loader
npm install --save-dev postcss-loader

エラー: Module build failed: Error: Cannot find module ‘node-sass’
対処:過去のコマンドを利用してインストール

npm install --save-dev node-sass

以上でWebpackのビルドがうまくいくようになりました

Ajax通信ライブラリ「axios」

axiosを利用し、ajax通信を行います。

\SPA\resources\js\bootstrap.js

window.axios = require('axios')

window.axios.defaults.headers.common = {
    'X-CSRF-TOKEN': window.Laravel.csrfToken,
    'X-Requested-With': 'XMLHttpRequest'
}

Vue.prototype.$http = window.axios

メインのjsファイルに処理を追加します。

\SPA\resources\js\app.js

window.Vue = require('vue');

import Vue from 'vue'
import VueRouter from 'vue-router'

require('./bootstrap')

Vue.use(VueRouter)

Vue.component('navbar', require('./components/Layouts/Navbar.vue'))

const router = new VueRouter({
    mode: 'history',
    routes: [
        { path: '/', component: require('./components/Articles/Index.vue') },
        { path: '/about', component: require('./components/About.vue') },
    ]
})

const app = new Vue({
    router,
    el: '#app'
})

npm run watch を起動していると、jsの編集に応じて自動ビルドが動く。今回は、まだ作成していない Index.vue About.vue を指定しているためエラーが表示されるが、気にしない。

Viewを作成

Laravelが返却する唯一のビューとなる「app.blade.php」を作成します。

\SPA\resources\views\app.blade.php    

<!DOCTYPE html>
<html lang="{{ config('app.locale') }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <link rel="stylesheet" href="{{ mix('css/app.css') }}"></script>

        <script>
            window.Laravel = {
                csrfToken: "{{ csrf_token() }}"
            };
        </script>
    </head>
    <body>
        <div id="app">
            <navbar></navbar>
            <div class="container">
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="{{ mix('js/app.js') }}"></script>
</html>

Vue Component作成

以下の2つのコンポーネントをそれぞれ作成します。

\SPA\resources\js\components\Layouts\Navbar.vue

<template>
    <nav class="navbar navbar-default">
        <li>
            <router-link  to="/about">About</router-link>
        </li>
    </nav>
</template>


\SPA\resources\js\components\About.vue

<template>
    <div>
        This page describe who we are.
    </div>
</template>

ajax通信を行う

VueからLaravelへajax通信を行います。
apiにアクセスしてメソッド名を取得するだけの処理となります。

\SPA\resources\js\components\Articles\Index.vue

<template>
    <div>
        <p>
            {{ name }}
        </p>
    </div>
</template>

<script>
    export default {
        created() {
            this.getApiName()
        },
        data() {
            return {
                name: ''
            }
        },
        methods: {
            getApiName() {
                this.$http.get('/api/spa')
                .then(res =>  {
                    this.name = res.data.data
                })
            }
        }
    }
</script>

実行してみる

以上で、LaravelとVueを使用したSPAが完成したはずなので、サイトにアクセスしてみます。
http://spa.test/
enter image description here

http://spa.test/about
enter image description here

かなり不格好ではありますが、ひとまずVue + axios でSPAのひな型を作成することができました。

終わりに

今回はLaravel + VueでSPAのひな型を作成してみました。
初めて使う技術ばかりなので、いろいろ躓きそうですが、今後はこれをもとに小さなWebアプリを作っていけたらなぁという感じです。
javaScriptもきちんと勉強しないと、すでに構文が意味不明ですねぇ・・・ (´・ω・`)