Laravel と Vue.js の連携(第3回/全3回)

Laravel は公式で Vue.js を推奨していますので、ぜひ活用してみましょう。
第1回の記事では Vue.js と Webpack について第2回の記事では Laravel と Vue.js のデータのやりとりについて見ていきました
最終回である第3回目はブラウザへの出力についてです。

Bladeで の JavaScript ファイル読み込み

ブラウザへの出力は基本的に Vue.js の機能に依ります。
まずは Blade テンプレートで JavaScript ファイルを読み込みます。
<script src="{{ assets('js/app.js') }}"></script>

Laravel では上記の読み込み方が一般的ですが、もう一つ方法があります。
以下の読み込み方です。
<script src="{{ mix('js/manifest.js') }}"></script>
<script src="{{ mix('js/vendor.js') }}"></script>
<script src="{{ mix('js/app.js') }}"></script>

mix('...')」だと何が違うかとういうと、JavaScript ファイルパスにバージョンIDを自動追加してくれる点と、依存ライブラリを分割してくれる点です。
以下でバージョンIDの仕組みと、なぜファイルを3つも読み込むのかをご説明します。

バージョンIDの自動追加

laravel-mix によって JavaScript がコンパイルされると、public/mix-manifest.jsonというファイルが自動出力されます。
mix('...') は、このmix-manifest.jsonに書かれた情報をもとに読み込みます。
mix-manifest.jsonの中には、各ファイルパスにブラウザキャッシュ用のIDを付加したデータが入ります。
(特に出力ファイル名を指定しない場合、ファイルパスのあとにパラメータとしてキャッシュIDが付加されます)

依存ライブラリの分割

Webpack によって、依存ライブラリもまとめて JavaScript にインクルードし1つのファイルにして出力することができます。
しかし、場合によってはファイルサイズが肥大化してしまい読み込みが遅くなる可能性があります。
そこで、laravel-mixにextract()メソッドを追加するのをおすすめします。
webpack.mix.jsに以下のように記述します。
const mix = require('laravel-mix');

mix
  .version() // <-- ブラウザキャッシュ用バージョン付加指定
  .extract() // <-- ファイル分割指定
  .js('resources/js/app.js', 'public/js')
  .sass('resources/sass/app.scss', 'public/css');

上記の指定で、サイト独自のスクリプトは app.js として出力、jQuery や Vue.js などの依存ライブラリ(変更がないファイル群)は vendor.js というファイルにまとめて出力されます。
加えて、この分割した各ファイルを関連付けるためのスクリプトが manifest.js として出力されます。
この処理により、vendor.js はブラウザにキャッシュさせ、変更のある app.js のみ再読み込みさせることで変更が反映されるようにします。

参考:Laravel Mix 公式 Versioning(バージョニング)
参考:Laravel Mix 公式 Library Code Splitting(コードの分割)

Vue.js の初期設定

Vue.js は、指定したHTMLの要素内で動作する形式になっています。
例として、Blade ファイル側に、以下のようなHTMLを用意します。
ここでは app というID名ですが、はなんでもかまいません。
<div id="app">
    <hello-world></hello-world>
</div>

<hello-world>というおかしなタグがありますが、ここに Vue.js で何かを出力するためのものなので、後述します。
この要素内でVue.jsが動作するように書いていきます。

Vue インスタンスの作成

resources/js/app.js に、以下のように記述します。
// [1] Vue.js ライブラリの読み込み
import Vue from 'vue';

// [2] コンポーネントの定義
Vue.component('hello-world', {
  // コンポーネントで出力するテンプレート
  template: '<section>Hello, {{ text }}!</section>',

  // 上記テンプレート内で使うデータ
  data: function () {
    return {
      text: 'World'
    };
  }
});

// [3] Vueインスタンスを作成
new Vue({
  el: '#app'
});

[1] Vue.js ライブラリの読み込み

Webpackにより import 機能でファイルをインポート(インクルード)します。
(Webpack はラップしているだけで、厳密には ES2015 の機能です)
これにより Vue という変数に Vue.js のクラス本体がセットされます。

[2] コンポーネントの定義

Vue.js は「コンポーネント」という単位で機能を作成していくのが基本です。
Blade に記述した <hello-world> というHTMLタグが「コンポーネントを出力する容れもの」となるため、同じ名前でコンポーネントを定義します。

コンポーネントの設定は JSON 形式のデータで記述します。

template に、<hello-world>へ出力するテンプレートを記述します。
Vue.js のテンプレートはHTML形式ですが、必ず全体を囲むタグ(ここでは<section>)が必要ですのでご注意ください。
参考:Vue.js 公式 テンプレート構文

テンプレート内で{{ text }}という部分がありますが、ここにdataで定義したデータを出力することができます。
dataの内容は JSON 形式ではなく関数として定義していますが、これは JavaScript の変数が「コピー」ではなく「参照」という仕様のためです。
コンポーネント内で独立した値(コピー)として使うためのもので、Vue.jsの決まり事となっています。

dataに「text」という名前で値を記述しており、これをテンプレート内で出力させます。

[3] Vueインスタンスを作成

インスタンスの作成はシンプルです。
Blade で <div id="app"> の要素を書いたので、その要素に対してインスタンスを作成するという意味です。

もしかしたら、[2] で定義したコンポーネントはここでは指定しないのかと思う方もいるかもしれません。
Vue.jsには「グローバルコンポーネント」と「ローカルコンポーネント」という2種類のコンポーネントがあります。
今回のものはどこでも使える「グローバルコンポーネント」の形式で定義しています。
全体で使う必要がない「ローカルコンポーネント」の場合、Vueインスタンス作成時(またはコンポーネント作成時)に指定します。
詳しい指定方法はこちらを参照ください。
参考:Vue.js 公式 コンポーネントの登録 / ローカル登録

コンパイルと動作確認

ここまででブラウザに出力する準備が整いましたので、laravel-mix でコンパイルします。
ターミナル等でプロジェクトルート(package.jsonファイルがある場所)に移動し、以下のコマンドを実行します。
npm run dev

これだけで laravel-mix が Webpack を呼び出し、各ファイルをコンパイルしてくれます。

コンパイルのコマンド内容はpackage.jsonscripts内に定義されており、いくつか種類がありますが、主なものは以下の3種類です。
  1. npm run dev
    コンパイルして終了
  2. npm run watch
    コンパイル後そのままファイルを監視し、変更があれば再コンパイル
  3. npm run prod
    コンパイルし、さらにファイルを圧縮して終了(本番公開用)
これでコンパイルされたファイルが出力されますので、ブラウザで確認してみます。
Laravel の artisan を使って PHP のビルトインサーバーを起動します。
php artisan serve

ブラウザで http://127.0.0.1 を開くと「Hello, World!」と出力されるのが確認できると思います。
Vue.js によって最終的にに出力されるHTMLは以下のようになるはずです。
(ブラウザの「ソース」ではなく開発者ツールで確認できます)
<div id="app">
    <section>Hello, World!</section>
</div>

以上で Laravel と Vue.js の初歩的な連携が完了です。
これまでjQueryのようなものを使っていた方なら、あまりにもやり方が違いすぎてハードルが高いかもしれません。
しかし、Webpack や Vue.js のような技術は学習コストをかけてでも覚える価値があると思いますので、ぜひチャレンジしてみてください。