この連載記事では、フロントエンドに Vue.js + Vue Router + Vuex とサーバーサイドに Laravel を使用したシングルページ Web アプリケーションの開発方法を紹介します。実際に写真共有アプリを開発する手順を通して SPA 開発のエッセンスを学ぶことができます。
今回のチュートリアルで扱うツールなどのバージョンは以下の通りです。
Node | npm | Vue.js | Vue Router | Vuex | PHP | Laravel |
---|---|---|---|---|---|---|
10.15 | 6.4 | 2.6 | 3.1 | 3.1 | 7.4 | 6.9 |
この章ではログイン画面のユーザーインターフェースを実装します。
基本的な Vue コンポーネントの機能を使ってページを構築する方法を学びましょう。
ヘッダーとフッター
まずはログイン画面へのリンクを配置するヘッダーとフッターを追加しましょう。
ヘッダーコンポーネント
resources/js/components
に Navbar.vue
を作成してください。
<template>
<nav class="navbar">
<RouterLink class="navbar__brand" to="/">
Vuesplash
</RouterLink>
<div class="navbar__menu">
<div class="navbar__item">
<button class="button">
<i class="icon ion-md-add"></i>
Submit a photo
</button>
</div>
<span class="navbar__item">
username
</span>
<div class="navbar__item">
<RouterLink class="button button--link" to="/login">
Login / Register
</RouterLink>
</div>
</div>
</nav>
</template>
このコンポーネントはヘッダーのナビゲーションバーです。
RouterLink
コンポーネントが初めて登場しています。
RouterLink
は RouterView
と同様、Vue Router から提供されているコンポーネントです。役割はアンカーリンクとほぼ同じで HTML 的にも <a>
要素が描画されるのですが、普通のアンカーリンクと挙動が大きく異なるのは通常の画面遷移(=サーバサイドへの GET リクエスト)が発生しないことです。RouterLink
で描画したリンクをクリックすると、フロントエンドでの画面遷移、つまり Vue Router によるコンポーネントの切り替わりが発生します。
サーバサイドへの通信回数を最適化して SPA の利点を活かすため、Vue Router の管理下にあるページへ遷移する場合は必ず RouterLink
を使用しましょう。
また、写真投稿ボタンとユーザー名表示箇所も表示されています。こちらは後続の章で認証状態による切り替え処理を追加します。つまりログイン済みであれば写真投稿ボタンとユーザー名を表示し、ログインしていなければログインリンクを表示します。
フッターコンポーネント
続いてフッターコンポーネントです。
resources/js/components
に Footer.vue
を作成してください。
<template>
<footer class="footer">
<button class="button button--link">Logout</button>
<RouterLink class="button button--link" to="/login">
Login / Register
</RouterLink>
</footer>
</template>
ログアウトボタンとログインページへのリンクが両方表示されていますが、ここには後ほど認証状態による切り替え処理を追加します。
App.vue
ルートコンポーネントである App.vue
で Navibar
と Footer
を使用します。
<template>
<div>
<header>
<Navbar />
</header>
<main>
<div class="container">
<RouterView />
</div>
</main>
<Footer />
</div>
</template>
<script>
import Navbar from './components/Navbar.vue'
import Footer from './components/Footer.vue'
export default {
components: {
Navbar,
Footer
}
}
</script>
ビルドコマンドを実行していなければ、実行してください。
$ npm run watch
ブラウザでヘッダーとフッターが表示されることを確認しましょう。
タブ機能を実装する
ここからログインページの UI を実装していきます。
まずはログインフォームと会員登録フォームを切り替えるタブを作成します。
タブ UI を追加する
resources/js/pages/Login.vue
を以下の通りに編集してください。
<template>
<div class="container--small">
<ul class="tab">
<li
class="tab__item"
@click="tab = 1"
>Login</li>
<li
class="tab__item"
@click="tab = 2"
>Register</li>
</ul>
{{ tab }}
</div>
</template>
<script>
export default {
data () {
return {
tab: 1
}
}
}
</script>
タブの機能を実現するために、tab
というデータ変数を用意しました。Login
タブをクリックすると tab
の値が1になり、Register
タブをクリックすると tab
の値は2になります。
コードが書けたら実際にそれぞれのタブをクリックして {{ tab }}
の部分の数字が切り替わることを確認してみましょう。
切り替わるコンテンツ
タブのクリックによってデータ変数 tab
の値が切り替えられたので、その値によって表示するコンテンツを追加します。v-show
を使えば tab
が特定の値のときのみ表示される要素を作成することができます。
{{ tab }}
の記述を削除して以下の通りに編集してください。
<ul class="tab">
<!-- 中略 -->
</ul>
<div class="panel" v-show="tab === 1">Login Form</div>
<div class="panel" v-show="tab === 2">Register Form</div>
<ul>
要素の下にタブによって表示が切り替わるコンテンツを追加します。
ブラウザで「Login Form」と「Register Form」がタブで切り替わることを確認しましょう。
選択状態のスタイルを変える
タブ UI の仕上げに、選択された状態のタブのスタイルを変更します。
それぞれのタブを表す <li>
要素に以下のように :class
を追記してください。
<ul class="tab">
<li
class="tab__item"
:class="{'tab__item--active': tab === 1 }"
@click="tab = 1"
>Login</li>
<li
class="tab__item"
:class="{'tab__item--active': tab === 2 }"
@click="tab = 2"
>Register</li>
</ul>
選択された状態のスタイルは tab__item--active
というクラスに定義しています。データ変数 tab
の値によってこのクラスの有無を切り替えています。
フォームを実装する
次にタブで切り替わるコンテンツの中身であるフォーム UI を実装します。
ログインフォーム
フォーム UI を追加する
「Login Form」と記載していた部分に以下のフォームを追記してください。
<div class="panel" v-show="tab === 1">
<form class="form">
<label for="login-email">Email</label>
<input type="text" class="form__item" id="login-email">
<label for="login-password">Password</label>
<input type="password" class="form__item" id="login-password">
<div class="form__button">
<button type="submit" class="button button--inverse">login</button>
</div>
</form>
</div>
ここまでは何の変哲も無い HTML フォームでしょう。
データ変数の紐づけ
フォームの入力値をスクリプト内部で参照するために v-model
を使って入力値とデータ変数を紐づけます。data
メソッドの返却値オブジェクトに以下の通り loginForm
を追加します。
export default {
data () {
return {
tab: 1,
loginForm: {
email: '',
password: ''
},
}
}
}
続いて <input>
要素に v-model
を追加しデータ変数を紐づけます。
<input type="text" class="form__item" id="login-email" v-model="loginForm.email">
<input type="password" class="form__item" id="login-password" v-model="loginForm.password">
送信メソッド
ログインフォーム作成の最後にデータを送信するためのメソッドを追加します。
実際の送信処理は後続の章で実装しますので、今は入力値をメソッドの中で参照できることを確かめるためのログ出力のみを記述しています。
export default {
data () {/* 中略 */},
methods: {
login () {
console.log(this.loginForm)
}
}
}
<form>
要素で、submit
イベントのハンドラとして上記の login
メソッドを指定します。
<form class="form" @submit.prevent="login">
@submit
に続く .prevent
はイベント修飾子と呼ばれます。.prevent
を記述することは、イベントハンドラで event.preventDefault()
を呼び出すのと同じ効果があります。これによりデフォルトのフォーム送信の挙動をキャンセルしページリロードを抑制します。
参考:イベント修飾子
ここまでできたら値を入力して login ボタンをクリックしてみましょう。
コンソールに入力値を含む loginForm
オブジェクトが表示されたでしょうか。
会員登録フォーム
会員登録フォームもログインフォームと同じパターンなので、コードはまとめて紹介します。
まずはテンプレート部分です。
「Register Form」と記載していた箇所を以下のフォームに書き換えます。
<div class="panel" v-show="tab === 2">
<form class="form" @submit.prevent="register">
<label for="username">Name</label>
<input type="text" class="form__item" id="username" v-model="registerForm.name">
<label for="email">Email</label>
<input type="text" class="form__item" id="email" v-model="registerForm.email">
<label for="password">Password</label>
<input type="password" class="form__item" id="password" v-model="registerForm.password">
<label for="password-confirmation">Password (confirm)</label>
<input type="password" class="form__item" id="password-confirmation" v-model="registerForm.password_confirmation">
<div class="form__button">
<button type="submit" class="button button--inverse">register</button>
</div>
</form>
</div>
そしてスクリプト部分です。
export default {
data () {
return {
tab: 1,
loginForm: {/* 中略 */},
registerForm: {
name: '',
email: '',
password: '',
password_confirmation: ''
}
}
},
methods: {
login () {/* 中略 */},
register () {
console.log(this.registerForm)
}
}
}
こちらもコードが書けたら値を入力して register ボタンをクリックしてみましょう。
コンソールに入力値を含む registerForm
オブジェクトが表示されたでしょうか。
この章はこれでおしまいです。
本章までのソースコードはリポジトリの ch-5 ブランチに置いてあります。
次の章では状態管理ライブラリ Vuex を導入し、フロントエンドから Web API を呼び出す処理を実装します。
関連記事
連載記事(全16回)
Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう
- (1) イントロダクション
- (2) アプリケーションの設計
- (3) SPA開発環境とVue Router
- (4) 認証API
- (5) 認証ページ
- (6) 認証機能とVuex
- (7) 認証機能とVuex Part.2
- (8) エラーハンドリング
- (9) 写真投稿API
- (10) 写真投稿フォーム
- (11) 写真一覧取得API
- (12) 写真一覧ページ
- (13) 写真詳細ページ
- (14) コメント投稿機能
- (15) いいね機能
- (16) エラーハンドリング Part.2