LaravelからVue.jsを使う最短レシピ - Qiita という記事を公開していたのですが、情報が古くそのままの手順で再現できませんでした。
そのため、Laravel7用にブラッシュアップした記事を書きたいと思います。
間違ったことが書いてあるとか、Laravelが新しくなってこの記事の通りにできなくなった等あれば
この記事にコメントをいただけると嬉しいです。
Laravel から最短でVue.jsを使う
インストールから動かして本番投入まで一通りっていうのが見つからなかったのでまとめてみます。
知らないとわかんないよそんなの!みたいな罠がそこかしこにありました。
TL;DR
- Vue(やReact)が別のパッケージになったので自分でインストールする
- bladeテンプレートの修正とコマンドをいくつか実行すれば使える
- bladeのいじるとこ
今回の環境
macOS Catalina
PHP 7.3.11 (composerがインストールされている)
Node.js (というよりnpm) v10.15.1(10系のLTSか12系のLTSなら良いはず)
Laravel 7.4
インストール
Installation - Laravel - The PHP Framework For Web Artisans
とかに従ってLaravelをインストールします。
# Laravel installerをグローバルインストールしてLaravelインストーラーからインストール
composer global require laravel/installer
laravel new blog
# Laravelのプロジェクトだけ作成
composer create-project --prefer-dist laravel/laravel blog
それから作成したプロジェクトに移動して laravel/ui を追加します。
cd blog
composer require laravel/ui
最後に、UIとしてVueを利用すると指定し、npm install
# php artisan ui vue --auth で認証系の画面を一緒に作ってくれるっぽい?
php artisan ui vue
npm install
インストールはこれでおしまい。
ちなみにvue以外にもreactも選択できます。
bladeの修正
ポイントは3つ。
<!-- Headタグ内に足す -->
<meta name="csrf-token" content="{{ csrf_token() }}">
Laravelがセキュリティを高めるために「ajax通信をするときはcsrfトークンをいつも使いましょうね」
っていうデフォルト設定がbootstrap.jsに書いてあるんだけどblade側にtokenが埋め込まれてないので埋め込みます。なんでだろう?
<!-- Headタグ内に足す -->
<link rel="stylesheet" href="{{ mix('/css/app.css') }}">
{{mix('パス')}} が、Laravelのアセットのパスをいい感じに解決してくれます。
<div id="app">
<!-- デフォルトだとこの中ではvue.jsが有効 -->
<!-- example-component はLaravelに入っているサンプルのコンポーネント -->
<example-component></example-component>
</div>
<!-- body タグの最後に足す-->
<script src=" {{ mix('js/app.js') }} "></script>
vue.jsのコンポーネントetcをロードするapp.jsは最後に読み込みましょう。
具体的には、すべてのコンポーネント用のタグ(今回だと #app)のあとに読み込まないと、
vue.jsのルートインスタンスが目的のDOMを捕まえられなくて死んでしまいます。
起動
Vue.js開発は、Laravelのartisan serveと別にコンポーネントのコンパイルが必要です。
ローカル環境で開発するなら下記のコマンドで開発すると幸せになれます。
npm run hot #vue cliでいうところの npm run dev相当。HMR!
npm run hot するとHot Module Replacement(HMR)有効な状態でビルドされます。。
つまりどういうことだってばよって、編集するとリアルタイムで結果が反映されてめっちゃ開発めっちゃ捗ります!
めちゃくちゃ開発が捗るようになるので是非。
また、開発/ステージングサーバーや本番にデプロイする時は以下のコマンドでビルドしましょう。
#開発デプロイ時
npm run dev # npm run develop のショートハンド
#本番デプロイ時
npm run prod # npm run production のショートハンド
やったねLaravelでVueが使えるようになったよ!
php artisan serve # Laravelの開発サーバーをlocalhostで立ち上げ
localhost:8000 にアクセスしてExample Componentが表示されていることを確認しましょう。
Laravel と Vue の変数展開がバッティングしている問題の解決方法
そもそも論、Betterな解決方法としてはBladeの中にVueのシンタックスを書くべきではないと思います。
ですが、Bladeだけ見た時の見通しの良さやチームメンバーのレベル感の問題で、
.vueファイルにコンポーネントに切り出さずにBladeファイル内に閉じてしまいたいと考えるかもしれません。
Laravel 7.4ではBladeの変数展開シンタックス{{ }}
は変更できないようです。
代わりに @{{ }}
とすることで、{{ }}を展開せずそのままレンダリングすることができます。
それでもやっぱり紛らわしい!という場合は、幸いVueの変数展開シンタックスを変更可能することが可能です。
resources/js/app.js
を開いてください。
const app = new Vue({
el: '#app',
delimiters: ["<%","%>"], // この行を追加
});
これで、Vue側のテンプレートでの変数展開シンタックスを変更することが可能です。
Laravelから変数を受け取る方法
最も原始的な方法は、サーバー側で変数をBladeに送り込み、
受け取ったBladeではJSONでパースしてWindowオブジェクトに突っ込む方法です。
Route::get('/', function () {
$data = [
"data" => ["name" => "Taro"] // dataがネストしてるのは間違いじゃない
];
return view('welcome', $data);
});
<!-- 前略 -->
<script>
window.data = @json($data)
</script>
</head>
<body>
<!-- 後略 -->
この方法は原始的ですが、もっともbladeらしいやり方ともいえます。
window.dataはグローバルに参照でき、破壊可能で、Vueによってリアクティブに監視されません。
Vueのコンポーネント内で利用するには、Vueコンポーネントのdata内で明示してあげたり、
メソッドの中で呼び出してあげる必要があるでしょう。
ちなみにこのサンプルでは上述のデリミタの変更は行っていません。
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Example Component</div>
<!-- この行でコンポーネントの name を表示してる -->
<div class="card-body">{{ name }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log("Component mounted.");
},
data() {
return {
name: window.data.name // この行を追加
};
}
};
</script>
こんな感じでできました。
グローバルスコープを汚すので、ちょっとお行儀が悪い気がしますね。
API から axios をつかってデータを取ってくる
まず適当にAPIをは生やしてみましょう
Route::get("/sample", function(){
$data = ["name" => "Jiro"];
return $data;
});
これで、 http://localhost:8000/api/sample へアクセスすると、
{"name":"Jiro"}
と表示されるのがわかると思います。
実態がDBへのアクセスを伴うものでも基本は変わりません。
それではVueからこのAPIを扱う方法を見ていきたいと思います。
API通信のためのAxiosはLaravelのデフォルトパッケージとしてインストールされています。
あとはコンポーネント内でそれをただ利用するだけです。
<script>
export default {
async mounted() {
console.log("Component mounted.");
const ret = await window.axios.get("/api/sample")
console.log(ret.data)
this.name = ret.data.name;
},
data() {
return {
name: window.data.name
};
}
};
</script>
これで、リロードしたときに最初はTaroが表示されていて、
APIの通信の結果をうけてJiroが表示されることがわかるでしょう!
以上が、LaravelでのAPI通信の基本となります。