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>

<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,
]);

/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');
    }
}

/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');
    }
}

/home

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

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

Vue Routerの導入

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

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

npm i -D vue-router

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

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

遅延ローディングルート | Vue Router
Vue.js の公式ルータ

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

resources/js/app.js
import router from './plugins/router'

const app = new Vue({
    el: '#app',
    router,
    template: '<router-view />',
});

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>

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

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

resources/js/app.js
Vue.component(
    'navbar-component',
    require('./components/NavbarComponent.vue').default
)

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

各ページに対応する〇〇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>

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>

こちらも 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>
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>

動作確認

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

次回は

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

コメント

タイトルとURLをコピーしました