ポータルサイトブログ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.json
のscripts
内に定義されており、いくつか種類がありますが、主なものは以下の3種類です。
npm run dev
コンパイルして終了npm run watch
コンパイル後そのままファイルを監視し、変更があれば再コンパイル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 のような技術は学習コストをかけてでも覚える価値があると思いますので、ぜひチャレンジしてみてください。