Laravelやってみる#4―Vue Routerの導入

Laravel 7.x

ぬにょす(挨拶)。

表題の通りですが、PHPフレームワークであるところのLaravelを使ってみようということで、やったこと・できたこと・できなかったこと等を自分の備忘録として残していきます。

#4の今回はBladeテンプレートをVueコンポーネントに変えていきます。

スポンサーリンク

Vue用のBladeテンプレート

Vueインスタンスを割り当てるBladeテンプレートを作成します。スカフォールドが生成した layouts/app.blade.phpをコピーして編集しました。

resources/views/vue.blade.php
<!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"></div> </body> </html>
Code language: HTML, XML (xml)

<div id=”app”></div>の部分にVueテンプレートが描画されます。

使用テンプレートの変更

Laravelフレームワークが使用するBladeテンプレートを vue.blade.php に変更します。

/

web.php で welcome.blade.php を使用するようになっているので、vue.blade.php を使用するように変更します。ついでに使わなそうな認証関係のルートを無効にしておきます。

routes/web.php
Route::get('/', function (){ // return view('welcome'); return view('vue'); }); // Auth::routes(); Auth::routes([ 'reset' => false, 'confirm' => false, 'verify' => false, ]);
Code language: PHP (php)

/login

LoginController が使用している AuthenticatesUsers トレイトに showLoginForm という関数があるので、これをオーバーライドします。

app/Http/Controllers/Auth/LoginController.php
class LoginController extends Controller { // >>>>> 以下の関数を追記 <<<<< /** * Show the application's login form. * * @return \Illuminate\Http\Response */ public function showLoginForm() { return view('vue'); } }
Code language: PHP (php)

/register

RegisterController が使用している RegistersUsers トレイトに showRegistrationForm という関数があるので、これをオーバーライドします。

app/Http/Controllers/Auth/RegisterController.php
class RegisterController extends Controller { // >>>>> 以下の関数を追記 <<<<< /** * Show the application registration form. * * @return \Illuminate\Http\Response */ public function showRegistrationForm() { return view('vue'); } }
Code language: PHP (php)

/home

HomeController の index アクションが home.blade.php を使用するようになっているので、vue.blade.php を使用するように変更します。

app/Http/Controllers/HomeController.php
public function index() { // return view('home'); return view('vue'); }
Code language: PHP (php)

Vue Routerの導入

アクセスルートに応じたVueコンポーネントを表示するために、Vue Routerを導入します。

npm で vue-router パッケージをインストールします。

npm i -D vue-router
Code language: plaintext (plaintext)

Vue Routerの設定ファイルを作成します。js/app.js にインポートできれば良いので、ファイルの格納場所は js/router.js でも js/router/index.js とかでも構いません。私はなんとなく追加プラグイン関係はまとめておきたいなーと思ったので js/plugins/router.js としました。

resources/js/plugins/router.js
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [ { path: '/', name: 'welcome', component: () => import(/* webpackChunkName: "js/welcome" */ '../views/WelcomeView.vue'), }, { path: '/login', name: 'login', component: () => import(/* webpackChunkName: "js/login" */ '../views/LoginView.vue'), }, { path: '/register', name: 'register', component: () => import(/* webpackChunkName: "js/register" */ '../views/RegisterView.vue'), }, { path: '/home', name: 'home', component: () => import(/* webpackChunkName: "js/home" */ '../views/HomeView.vue'), }, ] const router = new VueRouter({ mode: 'history', routes, }) export default router
Code language: JavaScript (javascript)

コンポーネントの読み込みは遅延ローディングにしています。

404 | Vue Router
The official Router for Vue.js

app.js にインポートし、Vueインスタンスに注入します。

resources/js/app.js
import router from './plugins/router' const app = new Vue({ el: '#app', router, template: '<router-view />', });
Code language: JavaScript (javascript)

Laravelフレームワークから返却される vue.blade.php に記述した <div id=”app”></div> の部分が、Vueインスタンスの template として指定した <router-view /> に書き換わり、Vue Router がルート定義に従ってURLに応じたコンポーネントを描画する、という動きになります(という認識です)。

ナビゲーションバーの部品化

ナビゲーションバーは複数の画面で使うので、部品化しておきます。

resources/js/components/NavbarComponent.vue
<template> <b-navbar toggleable="md" type="light" class="bg-white shadow-sm"> <b-container> <b-navbar-brand :to="{ name: 'welcome' }">Laravel</b-navbar-brand> <b-navbar-toggle target="navbarSupportedContent"></b-navbar-toggle> <b-collapse is-nav id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <b-navbar-nav class="mr-auto"></b-navbar-nav> <!-- Right Side Of Navbar --> <b-navbar-nav class="ml-auto"> <!-- Authentication Links --> <b-nav-item :to="{ name: 'login' }">Login</b-nav-item> <b-nav-item :to="{ name: 'register' }">Register</b-nav-item> <b-nav-item-dropdown right> <template v-slot:button-content>{{ user.name }}</template> <b-dropdown-item @click="logout">Logout</b-dropdown-item> </b-nav-item-dropdown> </b-navbar-nav> </b-collapse> </b-container> </b-navbar> </template> <script> export default { data: function(){ return { user: { name: "Username" } }; }, methods: { logout: function(){ console.log("logout"); } } }; </script> <style> </style>
Code language: HTML, XML (xml)

サーバーとデータをやり取りする部分の実装は、ひとまず置いておきます。

複数のページで使うので、グローバル登録しておきます。

resources/js/app.js
Vue.component( 'navbar-component', require('./components/NavbarComponent.vue').default )
Code language: JavaScript (javascript)

ページコンポーネントの作成

各ページに対応する〇〇View.vueは、Laravelのスカフォールドが生成したBladeテンプレートの内容をベースに、一部をBootstrap-Vueコンポーネントに置き換えるなどして <template> へ記述しました。

resources/js/views/WelcomeView.vue
<template> <div class="flex-center position-ref full-height"> <div class="top-right links"> <b-link :to="{ name: 'home' }">Home</b-link> <b-link :to="{ name: 'login'}">Login</b-link> <b-link :to="{ name: 'register' }">Register</b-link> </div> <div class="content"> <div class="title m-b-md">Laravel</div> <div class="links"> <a href="https://laravel.com/docs">Docs</a> <a href="https://laracasts.com">Laracasts</a> <a href="https://laravel-news.com">News</a> <a href="https://blog.laravel.com">Blog</a> <a href="https://nova.laravel.com">Nova</a> <a href="https://forge.laravel.com">Forge</a> <a href="https://vapor.laravel.com">Vapor</a> <a href="https://github.com/laravel/laravel">GitHub</a> </div> </div> </div> </template> <script> export default {}; </script> <style> </style>
Code language: HTML, XML (xml)

Welcome画面の見た目などは今後いじってくことになるので、今は気にしない方向。また、Bladeテンプレートのディレクティブは使えないので、ひとまずゴリッと削っておきます。なのでサーバーからのデータ注入や条件分岐は行われません。

resources/js/views/LoginView.vue
<template> <div> <navbar-component /> <main class="py-4"> <b-container> <b-row class="justify-content-center"> <b-col md="8"> <b-card header="Login"> <b-form @submit.prevent="login"> <b-row class="form-group"> <label for="email" class="col-md-4 col-form-label text-md-right">E-Mail Address</label> <b-col md="6"> <b-input v-model="form.email" :state="errors.email ? false : null" type="email" required autocomplete="email" autofocus /> <b-form-invalid-feedback :state="!(errors.email)">{{ errors.email }}</b-form-invalid-feedback> </b-col> </b-row> <b-row class="form-group"> <label for="password" class="col-md-4 col-form-label text-md-right">Password</label> <b-col md="6"> <b-input v-model="form.password" :state="errors.password ? false : null" type="password" required autocomplete="current-password" /> <b-form-invalid-feedback :state="!(errors.password)">{{ errors.password }}</b-form-invalid-feedback> </b-col> </b-row> <b-row class="form-group"> <b-col md="6" offset-md="4"> <b-form-checkbox v-model="form.remember">Remember Me</b-form-checkbox> </b-col> </b-row> <b-row class="form-group mb-0"> <b-col md="8" offset-md="4"> <b-button type="submit" variant="primary">Login</b-button> </b-col> </b-row> </b-form> </b-card> </b-col> </b-row> </b-container> </main> </div> </template> <script> export default { data: function(){ return { form: { email: "", password: "", remember: false }, errors: { email: "", password: "" } }; }, methods: { login: function(){ console.log("login"); } } }; </script> <style> </style>
Code language: HTML, XML (xml)

こちらも data や methods はプレースホルダー的な状態です。

resources/js/views/RegisterView.vue
<template> <div> <navbar-component /> <main class="py-4"> <b-container> <b-row class="justify-content-center"> <b-col md="8"> <b-card header="Register"> <b-form @submit.prevent="register"> <b-row class="form-group"> <label for="name" class="col-md-4 col-form-label text-md-right">Name</label> <b-col md="6"> <b-input v-model="form.name" :state="errors.name ? false : null" type="text" required autocomplete="name" autofocus /> <b-form-invalid-feedback :state="!(errors.name)">{{ errors.name }}</b-form-invalid-feedback> </b-col> </b-row> <b-row class="form-group"> <label for="email" class="col-md-4 col-form-label text-md-right">E-Mail Address</label> <b-col md="6"> <b-input v-model="form.email" :state="errors.email ? false : null" type="email" required autocomplete="email" /> <b-form-invalid-feedback :state="!(errors.email)">{{ errors.email }}</b-form-invalid-feedback> </b-col> </b-row> <b-row class="form-group"> <label for="password" class="col-md-4 col-form-label text-md-right">Password</label> <b-col md="6"> <b-input v-model="form.password" :state="errors.password ? false : null" type="password" required autocomplete="new-password" /> <b-form-invalid-feedback :state="!(errors.password)">{{ errors.password }}</b-form-invalid-feedback> </b-col> </b-row> <b-row class="form-group"> <label for="password-confirm" class="col-md-4 col-form-label text-md-right" >Confirm Password</label> <b-col md="6"> <b-input v-model="form.password_confirmation" type="password" required autocomplete="new-password" /> </b-col> </b-row> <b-row class="form-group mb-0"> <b-col md="8" offset-md="4"> <b-button type="submit" variant="primary">Register</b-button> </b-col> </b-row> </b-form> </b-card> </b-col> </b-row> </b-container> </main> </div> </template> <script> export default { data: function(){ return { form: { email: "", password: "", password_confirmation: "" }, errors: { name: "", email: "", password: "" } }; }, methods: { register: function(){ console.log("register"); } } }; </script> <style> </style>
Code language: HTML, XML (xml)
resources/js/views/HomeView.vue
<template> <div> <navbar-component /> <main class="py-4"> <b-container> <b-row class="justify-content-center"> <b-col md="8"> <b-card header="Dashboard"> <b-alert variant="success" show>You are logged in!</b-alert> </b-card> </b-col> </b-row> </b-container> </main> </div> </template> <script> export default { data: function(){ return {}; }, methods: {} }; </script> <style> </style>
Code language: HTML, XML (xml)

動作確認

Vue DevTools でコンポーネントが描画されていることを確認できました。

次回は

axiosでの認証処理を実装します。

コメント