Laravel 5.4で Vue.js開発環境を手軽に作る

こんにちは。宇都宮です。
最近はアシアル社内でもLaravelを使うことが増えてきました。また、フロントエンドも、ReactやVue.jsを使ったプロジェクトをちらほら見かけるようになってきました。
今回は、Laravelのインストール方法と、フロントエンド開発環境のセットアップ、簡単なVueコンポーネントの作り方を解説します。

Laravelとフロントエンド

Laravelでは、5.3から、Vue.jsが標準のJavaScriptフレームワークになりました。
さらに、5.4では、フロントエンドのビルドツールが、gulpベースのElixirから、WebpackベースのMixに変わりました。
Laravelをインストールすれば、Web APIはLaravelで作って、SPA(Single Page Application)をVue.jsで組む、といったことが簡単にできるようになっています。
なお、LaravelとVue.jsが密結合しているわけではないので、Vue.jsではなくReactやAngularを使うこともできます。

インストール

本記事では、Laravel 5.4.15を使用します。
また、Laravel 5.4の動作には、PHP 5.6.4以上と、いくつかのPHP拡張モジュールが必要です。

インストールにはいくつかの方法がありますが、以下ではcomposerを使用します。


composer create-project --prefer-dist laravel/laravel sample

上記コマンドを使用すると、現在のディレクトリに「sample」という名前のディレクトリが追加され、その中にLaravelアプリケーションが作成されます。
作成直後のディレクトリ構造は以下のようになります。


$ tree -L 1
.
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── package.json
├── phpunit.xml
├── public
├── readme.md
├── resources
├── routes
├── server.php
├── storage
├── tests
├── vendor
└── webpack.mix.js
10 directories, 8 files

まずは、Laravelアプリケーションが正常に動くか確認するため、開発用Webサーバを起動してみましょう。


$ php artisan serve
Laravel development server started: <http://127.0.0.1:8000>

次に、ブラウザで「 http://127.0.0.1:8000 」にアクセスしてみましょう。
以下のような画面が表示されれば正常に動作しています。

フロントエンドのセットアップ

Laravel 5.4をインストールすると、以下の内容のpackage.jsonが付いてきます。


{
  "private": true,
  "scripts": {
  // 省略
  },
  "devDependencies": {
    "axios": "^0.15.3",
    "bootstrap-sass": "^3.3.7",
    "jquery": "^3.1.1",
    "laravel-mix": "^0.8.1",
    "lodash": "^4.17.4",
    "vue": "^2.1.10"
  }
}

Laravelをインストールしたディレクトリで「npm install」を実行すれば、必要なライブラリが入ります。
npmのために、事前にNode.jsをインストールしておきましょう。

インストールが完了すると、以下の環境が作成されます。

    • ES2015 => ES5へのトランスパイル

 

    • Vueコンポーネントのコンパイル

 

    • Sass => CSSへのコンパイル

さきほど省略したpackage.jsonには、npmで実行できるスクリプトが定義されています。たとえば、上記コンパイル処理を一括で実行する際は「npm run dev」を使用します。

注意点として、2017年3月15日現在、「npm run dev」はcross-env.jsのパスの問題で失敗する可能性があります。
GitHubにIssueが挙がっているので、将来的には直っているはずです。
取り急ぎの修正方法としては、cross-env.jsのパスを修正して、以下のようにしましょう。


  "scripts": {
    "dev": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --watch-poll --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "hot": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "production": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },

scriptを修正して「npm run dev」を実行するとコンパイルが成功するはずです。

これで、public/js/app.jsにコンパイル済みのJavaScript、public/css/app.cssにコンパイル済みのCSSが出力されるようになります。
また、「npm run watch」を走らせておくと、JavaScriptやCSSを追加・変更した際に自動的にコンパイルが行われるようになります。

まずはVue.jsが使えるようになっているか確認しましょう。
resources/views/welcome.blade.php を以下の内容で置き換えます。


<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello Vue</title>
    <link rel="stylesheet" href="css/app.css"/>
    <script type="text/javascript">
        window.Laravel = window.Laravel || {};
        window.Laravel.csrfToken = "{{csrf_token()}}";
    </script>
</head>
<body>
<div id="app">
    <example></example>
</div>
<script src="js/app.js"></script>
</body>
</html>

以下のように表示されればOKです。

ここでは、Laravelに付属するExampleという名前のVueコンポーネントを使用しています。
また、resources/assets/js/bootstrap.jsで参照している「window.Laravel.csrfToken」という変数の定義も行っています。この変数の使いみちについては後で説明します。

JavaScriptアプリケーションのブートストラップ

resources/assets/js/bootstrap.js に、JavaScriptアプリケーションを動作させるのに必要な初期化処理を記述します。


window._ = require('lodash');
/**
 * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 * for JavaScript based Bootstrap features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */
window.$ = window.jQuery = require('jquery');
require('bootstrap-sass');
/**
 * Vue is a modern JavaScript library for building interactive web interfaces
 * using reactive data binding and reusable components. Vue's API is clean
 * and simple, leaving you to focus on building your next great project.
 */
window.Vue = require('vue');
/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */
window.axios = require('axios');
window.axios.defaults.headers.common = {
    'X-CSRF-TOKEN': window.Laravel.csrfToken,
    'X-Requested-With': 'XMLHttpRequest'
};
/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */
// import Echo from "laravel-echo"
// window.Echo = new Echo({
//     broadcaster: 'pusher',
//     key: 'your-pusher-key'
// });

ほとんどライブラリを読み込んでいるだけですが、1つ興味深い処理が行われています。


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

axiosは、HTTPクライアントのライブラリです。
ここでは、axoisの設定を変更し、リクエストヘッダにCSRFトークンと、XMLHttpRequestの目印を付けるようにしています。
このようにすることで、axiosを使って送られたリクエストに自動的にCSRFトークンが付与されるようになります。

Vue.jsアプリケーションの起動

アプリケーションの起動処理等は resources/assets/js/app.js に記述します。
ここは以下のようになっています。


/**
 * First we will load all of this project's JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */
require('./bootstrap');
/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */
Vue.component('example', require('./components/Example.vue'));
const app = new Vue({
    el: '#app'
});

(1) bootstrap.jsを読み込んで初期化処理を実行
(2) Example.vueというVueコンポーネントを読み込み
(3) Vueアプリケーションを起動

という流れです。

Vue.jsアプリケーションを起動する際には、どの要素をVue.jsアプリケーションのルートとするかを指定します。ここでは、idが"app"である要素がVue.jsアプリケーションのルートになるように指定しています。

Vue.jsでは、「コンポーネント」という単位で、再利用可能なパーツを定義することができます。
resources/assets/js/components/Example.vueは以下の内容になっています。


<template>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Example Component</div>
                    <div class="panel-body">
                        I'm an example component!
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

templateタグの中には、コンポーネントのHTMLを記述します。同様に、scriptタグの中には、コンポーネントの動作を定義します。ここでは使用されていませんが、styleタグを使用して、コンポーネントに適用するCSSを定義することもできます。

独自コンポーネントの定義

次に、独自のVueコンポーネントを定義してみましょう。
resources/assets/js/components/Hello.vueというファイルを、以下の内容で追加します。


<template>
    <h2>Hello <span class="name">{{name}}</h2>!</div>
</template>
<script>
    export default {
        props: ['name']
    }
</script>
<style scoped>
    .name {
        font-weight: bold;
    }
</style>

ここでは、Vue.jsのコンポーネントに、外部から値を渡せるように実装しています。

はじめに、コンポーネントがもつプロパティを、propsプロパティの中に配列で定義します。
次に、コンポーネントを利用する箇所で「<hello name="Laravel"></hello>」のように、コンポーネントに渡したい値を記述します。
propsに定義したプロパティはコンポーネント内から参照できるので、{{name}} のように記述すると文字列としてプロパティの値を出力することができます。

※{{}}という記法はLaravelのテンプレートエンジン(Blade)でも値を出力する際に使用します。Vueコンポーネント内で{{}}を使用する際は気にする必要はありませんが、Bladeテンプレートの中でVue.jsの{{}}を使いたい場合「@{{}}」という風に先頭に@をつけると、Bladeはこの部分を無視します。

style要素にscoped属性をつけている点にも注目です。scoped属性を使うと、style要素内で定義されたスタイルが適用される範囲を限定することができます。ここでは、Vueコンポーネントのテンプレート内でのみ適用可能な「name」クラスを定義しています。テンプレートの外部で「name」クラスを使った場合には、このスタイルは適用されません。

次に、app.jsにHelloコンポーネントを登録します。


Vue.component('hello', require('./components/Hello.vue'));

最後に、welcome.blade.phpのbodyの中を以下のように書き換えます。


<div id="app">
    <hello></hello>
    <hello name="Laravel"></hello>
    <hello name="Vue.js"></hello>
</div>

以下のように表示されればOKです。

参考