前 Vue.js入門 #06 3章 コンポーネントの基礎(コンポーネント間の通信/スロットコンテンツ/ログインフォーム)
次 Vue.js入門 #08 4章 Vue Routerの高度な機能
Vue Routerによるシングルページアプリケーション
シングルページアプリケーションとルーティング
- シングルページアプリケーション(SPA)とは、はじめにHTMLをロードして、以後はAjaxで情報を取得し動的にページを更新するWebアプリケーションのこと。
- SPAではページ遷移をクライアントサイドで行う。その際にAjaxを使用して、必要なデータを取得してViewの表示を行う。
- SPAを実装するには、以下のことを考慮する必要がある。
- クライアントサイドでの履歴管理なども含めたページ遷移
- 非同期によるデータ取得
- Viewのレンダリング
- モジュール化されたコードの管理
Vue Routerとは
- Vue Routerは、Vue.jsの公式のプラグインとして提供されているSPA構築のためのルーティングライブラリ。
- Vue Routerは、基本的なページ遷移の他にも以下のような機能がある。
- ネストしたルーティング
- リダイレクトとエイリアス
- HTML5 History APIとURL Hashによる履歴管理(IE9での自動的なフォールバック)
- 自動的にCSSクラスがアクティブになるリンクの仕組み
- Vue.jsのトランジションの仕組みを使ったページ遷移のトランジション
- スクロールの振る舞いのカスタマイズ
ルーティングの基礎
ルーターのインストール
<script src="https://unpkg.com/vue@2.5.17"></script>
<script src="https://unpkg.com/vue-router@3.0.1"></script>
ルーティング設計
- Vue Routerにおけるルートとは、Vue.jsのコンポーネントを特定のURLにマッピングしたオブジェクト。これをルーターコンストラクタを用いたルーター初期化時のroutesオプションに設定する。
// ルート定義
{
path: '/someurl', // URLを指定 ファイル名#/someurlでアクセスできる
component: {
template: '...' // コンポーネントの構文、またはコンストラクタベースのコンポーネント
}
}
// ルーターコンストラクタ、これを new Vue()に渡す
new VueRouter({
routes: [ ] // ルート定義を配列で渡す。
})
<!-- Vue.jsとVue Routerを読み込んでおく -->
<script>
// ルートオプションを渡してルーターインスタンスを生成
var router = new VueRouter({
// コンポーネントをマッピングしたルート定義を配列で渡す
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
template: '<div>ユーザー一覧ページです。</div>'
}
]
})
var app = new Vue({
router: router
}).$mount('#app')
</script>
HTML側の指定とページ遷移の実行
HTML側
-
<router-link to="/top">
タグでリンク先を指定する。デフォルトでは<a>
タグとしてレンダリングされる。 - ルート内でマッピングしたコンポーネントは
<router-view></router-view>
にレンダリングされる。
<script src="https://unpkg.com/vue@2.5.17"></script>
<script src="https://unpkg.com/vue-router@3.0.1"></script>
<div id="app">
<router-link to="/top">トップページ</router-link>
<router-link to="/users">ユーザー一覧ページ</router-link>
<router-view></router-view>
</div>
- VueRouterインスタンス引数にpathとcomponentがセットの配列を渡す。
- Vueインスタンスの引数に、routerインスタンスを渡す。
- #appをVueインスタンスにマウントする。
// ルートオプションを渡してルーターインスタンスを生成
var router = new VueRouter({
routes: [
{
path: '/top',
component: {
template: '<div>トップページです</div>'
}
},
{
path: '/users',
component: {
template: '<div>ユーザー一覧ページです。</div>'
}
}
]
})
var app = new Vue({
router: router
}).$mount('#app')
- それぞれのリンクをクリックすると表示が切り替わることを確認。
実践的なルーティングのための機能
URLパラメーターの扱いとパターンマッチング
- path上のURLにコロン付きのパターンで指定することで、URLからパラメータを取得できる。
-
user/123
のとき、$route.params.userid
で123を取得できる。
var router = new VueRouter({
routes: [
{
path: '/user/:userid',
component: {
template: '<div>ユーザーIDは {{ $route.params.userid }}</div>'
}
}
]
})
名前付きルート
- 名前付きルートを呼び出すには、(コロン付きの)
:to
に、パラメータで指定できる。
<script src="https://unpkg.com/vue@2.5.17"></script>
<script src="https://unpkg.com/vue-router@3.0.1"></script>
<div id="app">
<router-link :to="{ name: 'user', params: { userId: 123 }}">ユーザー詳細ページ</router-link>
<router-view></router-view>
</div>
-
name: 'user'
のように名前を指定する。
var router = new VueRouter({
routes: [
{
path: '/user/:userid',
name: 'user',
component: {
template: '<div>ユーザーIDは {{ $route.params.userId }}です。</div>'
}
}
]
})
var app = new Vue({
router: router
}).$mount('#app')
router.pushを使った遷移
<router-link>
は宣言的な書き方。router.push
を使うとプログラム上で動的な遷移が可能になる。
router.push({ name: 'user', params: { userId: 123 }})
フック関数
グローバルのフック関数
- 全てのページ遷移に対して設定できるフック関数。
- router.beforeEachに関数をセットすると、ページ遷移が起こる直前にその関数が実行される。
- 引数のfrom、toには遷移しようとしているルーティングの遷移元・遷移先ルートの情報が入っている。
- 以下は、
/users
に遷移しようとすると/top
に遷移され、それ以外は通常通りの遷移が行われる。
router.beforeEach(function (to, from, next)) {
// ユーザー一覧ページへアクセスしたときに/topへリダイレクトされる例
if (to.path === '/users') {
next('/top')
} else {
// 引数無しでnextを呼び出すと通常通りの遷移が行われる
next()
}
}
ルート単位のフック関数
- 全ての遷移ではなく、ルート単位でフックを追加するには、Vue Route初期化時のルート定義の時に個別に指定する。
- ルート定義にbeforeEnterを記述することで、ルーティング前のフックを追加する。
var router = new VueRouter({
routes: [
{
path: '/users',
component: UserList,
beforeEnter: function (to, from, next) {
// /users?redirect=true でアクセスされた時だけにtopにリダイレクトするフック関数を追加
if (to.query.redirect === 'true') {
next('/top')
} else {
next()
}
}
}
]
})
コンポーネント内のフック関数
- ルート定義ではなく、コンポーネント側でもフック関数は定義できる。
- コンポーネントのオプションとして、
beforeRouteEnter
を使ってデータを取得する。
var UserList = {
template: '#user-list',
data: function() {
return {
users: function () { return [] },
error: null
}
},
// 「ページ遷移が行われて、コンポーネントが初期化される」前に呼び出される
beforeRouteEnter: function (to, from, next) {
getUsers((function (err, users) {
if (err) {
this.error = err.toString()
} else {
// nextに渡すcallbackでコンポーネント自身にアクセス可
next(function (vm) {
vm.users = users
})
}
}).bind(this))
}
}
- このほかに、フック関数
beforeRouteLeave
は遷移の発生によりコンポーネントが去っていく時に呼ばれ、例えば未保存のページから遷移しようとするタイミングでconfirmを出力することが可能になる。
サンプルアプリケーションの実装
- /top(トップ)
- /users(ユーザー一覧) → /users/:id(ユーザー詳細)
- /login(新規ログインなし) ログイン後↓
- /users/new(新規ログイン済み)
- 今回のサンプルアプリケーションのベースとなるHTML
<script src="https://unpkg.com/vue@2.5.17"></script>
<script src="https://unpkg.com/vue-router@3.0.1"></script>
<div id="app">
<nav>
<router-link to="/top">トップページ</router-link>
<router-link to="/users">ユーザー一覧ページ</router-link>
</nav>
<router-view></router-view>
</div>
リストページの実装
- ユーザー一覧のテンプレート
<script type="text/x-template" id="user-list">
<div>ユーザー一覧ページ</div>
</script>
- ユーザー一覧のコンポーネントで、上記テンプレートを指定する。
// ユーザーリストコンポーネント
var UserList = {
template: '#user-list',
}
- ルーターに「
/top
と<div>トップページです。</div>
」、「/usersとユーザー一覧テンプレート」をマッピングする。
var router = new VueRouter({
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
component: UserList
}
]
})
// appとルーターをマウントする。
var app = new Vue({
router: router
}).$mount('#app')
</script>
APIによるデータ通信
- SPAでは、ページ遷移をした際に、APIを通じて取得したデータをUIに表示することが頻繁にある。
- Vue Routerでは、非同期通信によるデータ取得を行う場合は、createdとwatchを使って実装するのが一般的。
- watchは、算出プロパティを汎用的にしたVueコンポーネントのオプション。
- ユーザー一覧ページに切り替えたタイミングでユーザーの情報を取得して表示する。
<script type="text/x-template" id="user-list">
<div>
<div class="loading" v-if="loading">読み込み中...</div>
<div v-if="error" class="error">
{{ error }}
</div>
<!-- usersがロードされたら各ユーザーの名前を表示する -->
<div v-for="user in users" :key="user.id">
<h2>{{ user.name }}</h2>
</div>
</div>
</script>
// JSONを返す関数
// 擬似的にAPI経由で情報を取得したようにする
var getUsers = function (callback) {
setTimeout(function () {
callback(null, [
{
id: 1,
name: 'Takuya Tajima'
},
{
id: 2,
name: 'Yohei Noda'
}
])
}, 1000)
}
// ユーザーリストコンポーネント
var UserList = {
template: '#user-list',
data: function () {
return {
loading: false,
users: function () {
return []
},
error: null
}
},
created: function () {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData: function () {
this.loading = true
getUsers((function (err, users) {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.users = users
}
}).bind(this))
}
}
}
var router = new VueRouter({
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
component: UserList
}
]
})
var app = new Vue({
router: router
}).$mount('#app')
- ユーザー一覧の画面が出力される。
詳細ページの実装
- ユーザー詳細のテンプレートを追加する。
<!-- ユーザー詳細ページのテンプレート -->
<script type="text/x-template" id="user-detail">
<div>
<div class="loading" v-if="loading">読み込み中...</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="user">
<h2>{{ user.name }}</h2>
<p>{{ user.description }}</p>
</div>
</div>
</script>
-
userData
のデータを追加する。 -
getUser
関数を追加する。 -
UserDetail
コンポーネントを追加する。
// ダミーデータの定義。本来はデータベースの情報をAPI経由で取得する
var userData = [
{
id: 1,
name: 'Takuya Tejima',
description: '東南アジアで働くエンジニアです。'
},
{
id: 2,
name: 'Yohei Noda',
description: 'アウトドア・フットサルが趣味のエンジニアです。'
}
]
var getUser = function (userId, callback) {
setTimeout(function () {
var filteredUsers = userData.filter(function (user) {
return user.id === parseInt(userId, 10)
})
callback(null, filteredUsers && filteredUsers[0])
}, 1000)
}
// ユーザー詳細コンポーネント
var UserDetail = {
template: '#user-detail',
data: function () {
return {
loading: false,
user: null,
error: null
}
},
created: function () {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData: function () {
this.loading = true
// this.$route.params.userId に現在のURL上のパラメーターに対応したuserIdが格納される
getUser(this.$route.params.userId, (function (err, user) {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.user = user
}
}).bind(this))
}
}
}
var router = new VueRouter({
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
component: UserList
},
{
// /users/newの前にこのルートを定義するとパターンマッチにより/users/newが動作しなくなるので注意
path: '/users/:userId',
component: UserDetail
}
]
})
var app = new Vue({
router: router
}).$mount('#app')
ユーザー登録ページの実装
- ルーター部分に
/users/new
を追加する。
var router = new VueRouter({
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
component: UserList
},
{
path: '/users/new',
component: UserCreate
},
{
// /users/newの前にこのルートを定義するとパターンマッチにより/users/newが動作しなくなるので注意
path: '/users/:userId',
component: UserDetail
}
]
})
- ユーザー作成ページのテンプレートを追加する。
<script type="x-template" id="user-create">
<div>
<div class="sending" v-if="sending">Sending...</div>
<div>
<h2>新規ユーザー作成</h2>
<div>
<label>名前: </label>
<input type="text" v-model="user.name">
</div>
<div>
<label>説明文: </label>
<textarea v-model="user.description"></textarea>
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div>
<input type="button" @click="createUser" value="送信">
</div>
</div>
</div>
</script>
- postUser関数を追加する。擬似的にサーバーに投げるような関数。
- UserCreate(新規ユーザー)コンポーネント
// 擬似的にAPI経由で情報を更新したようにする
// 実際のWebアプリケーションではServerへPOSTリクエストを行う
var postUser = function (params, callback) {
setTimeout(function () {
// idは追加されるごとに自動的にincrementされていく
params.id = userData.length + 1
userData.push(params)
callback(null, params)
}, 1000)
}
// 新規ユーザー作成コンポーネント
var UserCreate = {
template: '#user-create',
data: function () {
return {
sending: false,
user: this.defaultUser(),
error: null
}
},
created: function () {
},
methods: {
defaultUser: function () {
return {
name: '',
description: ''
}
},
createUser: function () {
// 入力パラメーターのバリデーション
if (this.user.name.trim() === '') {
this.error = 'Nameは必須です'
return
}
if (this.user.description.trim() === '') {
this.error = 'Descriptionは必須です'
return
}
postUser(this.user, (function (err, user) {
this.sending = false
if (err) {
this.error = err.toString()
} else {
this.error = null
// デフォルトでフォームをリセット
this.user = this.defaultUser()
alert('新規ユーザーが登録されました')
// ユーザー一覧ページに戻る
this.$router.push('/users')
}
}).bind(this))
}
}
}
ログイン・ログアウトの実装
- Authメソッドの追加。
-
email === 'vue@example.com' && pass === 'vue'
のとき、ログインが成功する。
-
// サンプルアプリケーション用のダミー認証モジュール
var Auth = {
login: function (email, pass, cb) {
// ダミーデータを使った擬似ログイン
setTimeout(function () {
if (email === 'vue@example.com' && pass === 'vue') {
// ログイン成功時はローカルストレージにtokenを保存する
localStorage.token = Math.random().toString(36).substring(7)
if (cb) { cb(true) }
} else {
if (cb) { cb(false) }
}
}, 0)
},
logout: function () {
delete localStorage.token
},
loggedIn: function () {
// ローカルストレージにtokenがあればログイン状態とみなす
return !!localStorage.token
}
}
- ルーターの修正。
-
/users/new
に、beforeEnter(ルーティング前)のフック関数を追加する。 - /login、/logoutを追加する。
-
var router = new VueRouter({
// 各ルートにコンポーネントをマッピング
// コンポーネントはVue.extend() によって作られたコンポーネントコンストラクタでも
// コンポーネントオプションのオブジェクトでも渡せる
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
component: UserList
},
{
path: '/users/new',
component: UserCreate,
beforeEnter: function (to, from, next) {
// 認証されていない状態でアクセスした時はloginページに遷移する
if (!Auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
// 認証済みであればそのまま新規ユーザー作成ページへ進む
next()
}
}
},
{
// /users/newの前にこのルートを定義するとパターンマッチにより/users/newが動作しなくなるので注意
path: '/users/:userId',
component: UserDetail
},
{
path: '/login',
component: Login
},
{
path: '/logout',
beforeEnter: function (to, from, next) {
Auth.logout()
next('/')
}
}
]
})
var app = new Vue({
data: { // dataにAuthを追加する
Auth: Auth
},
router: router
}).$mount('#app')
ログインコンポーネントの作成
- ログインテンプレートの作成
<!-- ログインページのテンプレート -->
<script type="x-template" id="login">
<div>
<h2>Login</h2>
<p v-if="$route.query.redirect">
ログインしてください
</p>
<form @submit.prevent="login">
<label><input v-model="email" placeholder="email"></label>
<label><input v-model="pass" placeholder="password" type="password"></label><br>
<button type="submit">ログイン</button>
<p v-if="error" class="error">ログインに失敗しました</p>
</form>
</div>
</script>
- 上記テンプレートを、以下の
Login
コンポーネントで指定する。
// ログインコンポーネント
var Login = {
template: '#login',
data: function () {
return {
email: 'vue@example.com',
pass: '',
error: false
}
},
methods: {
login: function () {
Auth.login(this.email, this.pass, (function (loggedIn) {
if (!loggedIn) {
this.error = true
} else {
// redirectパラメーターが付いている場合はそのパスに遷移
this.$router.replace(this.$route.query.redirect || '/')
}
}).bind(this))
}
}
}
- グローバルメニューに、作成した全てのリンクを追加する。
v-show
を使って、ログイン状態で表示/非表示を切り替える。
<div id="app">
<nav v-cloak>
<router-link to="/top">トップページ</router-link>
<router-link to="/users">ユーザー一覧ページ</router-link>
<router-link to="/users/new?redirect=true">新規ユーザー登録</router-link>
<router-link to="/login" v-show="!Auth.loggedIn()">ログイン</router-link>
<router-link to="/logout" v-show="Auth.loggedIn()">ログアウト</router-link>
</nav>
<router-view></router-view>
</div>
- ログイン画面
- 新規ユーザー作成画面
- 登録されたことを確認
サンプルアプリケーションの全体像
HTML
<div id="app">
<nav v-cloak>
<router-link to="/top">トップページ</router-link>
<router-link to="/users">ユーザー一覧ページ</router-link>
<router-link to="/users/new?redirect=true">新規ユーザー登録</router-link>
<router-link to="/login" v-show="!Auth.loggedIn()">ログイン</router-link>
<router-link to="/logout" v-show="Auth.loggedIn()">ログアウト</router-link>
</nav>
<router-view></router-view>
</div>
<!-- ユーザー一覧ページのテンプレート -->
<script type="x-template" id="user-list">
<div>
<div class="loading" v-if="loading">読み込み中...</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-for="user in users" :key="user.id">
<router-link :to="{ path: '/users/' + user.id }">{{ user.name }}</router-link>
</div>
</div>
</script>
<!-- ユーザー詳細ページのテンプレート -->
<script type="x-template" id="user-detail">
<div>
<div class="loading" v-if="loading">読み込み中...</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="user">
<h2>{{ user.name }}</h2>
<p>{{ user.description }}</p>
</div>
</div>
</script>
<!-- ユーザー作成ページのテンプレート -->
<script type="x-template" id="user-create">
<div>
<div class="sending" v-if="sending">Sending...</div>
<div>
<h2>新規ユーザー作成</h2>
<div>
<label>名前: </label>
<input type="text" v-model="user.name">
</div>
<div>
<label>説明文: </label>
<textarea v-model="user.description"></textarea>
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div>
<input type="button" @click="createUser" value="送信">
</div>
</div>
</div>
</script>
<!-- ログインページのテンプレート -->
<script type="x-template" id="login">
<div>
<h2>Login</h2>
<p v-if="$route.query.redirect">
ログインしてください
</p>
<form @submit.prevent="login">
<label><input v-model="email" placeholder="email"></label>
<label><input v-model="pass" placeholder="password" type="password"></label><br>
<button type="submit">ログイン</button>
<p v-if="error" class="error">ログインに失敗しました</p>
</form>
</div>
</script>
JavaScript
// サンプルアプリケーション用のダミー認証モジュール
var Auth = {
login: function (email, pass, cb) {
// ダミーデータを使った擬似ログイン
setTimeout(function () {
if (email === 'vue@example.com' && pass === 'vue') {
// ログイン成功時はローカルストレージにtokenを保存する
localStorage.token = Math.random().toString(36).substring(7)
if (cb) { cb(true) }
} else {
if (cb) { cb(false) }
}
}, 0)
},
logout: function () {
delete localStorage.token
},
loggedIn: function () {
// ローカルストレージにtokenがあればログイン状態とみなす
return !!localStorage.token
}
}
// ダミーデータの定義。本来はデータベースの情報をAPI経由で取得する
var userData = [
{
id: 1,
name: 'Takuya Tejima',
description: '東南アジアで働くエンジニアです。'
},
{
id: 2,
name: 'Yohei Noda',
description: 'アウトドア・フットサルが趣味のエンジニアです。'
}
]
// 擬似的にAPI経由で情報を取得したようにする
var getUsers = function (callback) {
setTimeout(function () {
callback(null, userData)
}, 1000)
}
var getUser = function (userId, callback) {
setTimeout(function () {
var filteredUsers = userData.filter(function (user) {
return user.id === parseInt(userId, 10)
})
callback(null, filteredUsers && filteredUsers[0])
}, 1000)
}
// 擬似的にAPI経由で情報を更新したようにする
// 実際のWebアプリケーションではServerへPOSTリクエストを行う
var postUser = function (params, callback) {
setTimeout(function () {
// idは追加されるごとに自動的にincrementされていく
params.id = userData.length + 1
userData.push(params)
callback(null, params)
}, 1000)
}
// ログインコンポーネント
var Login = {
template: '#login',
data: function () {
return {
email: 'vue@example.com',
pass: '',
error: false
}
},
methods: {
login: function () {
Auth.login(this.email, this.pass, (function (loggedIn) {
if (!loggedIn) {
this.error = true
} else {
// redirectパラメーターが付いている場合はそのパスに遷移
this.$router.replace(this.$route.query.redirect || '/')
}
}).bind(this))
}
}
}
// ユーザーリストコンポーネント
var UserList = {
template: '#user-list',
data: function () {
return {
loading: false,
users: function () {
return []
},
error: null
}
},
created: function () {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData: function () {
this.loading = true
getUsers((function (err, users) {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.users = users
}
}).bind(this))
}
}
}
// ユーザー詳細コンポーネント
var UserDetail = {
template: '#user-detail',
data: function () {
return {
loading: false,
user: null,
error: null
}
},
created: function () {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData: function () {
this.loading = true
// this.$route.params.userId に現在のURL上のパラメーターに対応したuserIdが格納される
getUser(this.$route.params.userId, (function (err, user) {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.user = user
}
}).bind(this))
}
}
}
// 新規ユーザー作成コンポーネント
var UserCreate = {
template: '#user-create',
data: function () {
return {
sending: false,
user: this.defaultUser(),
error: null
}
},
created: function () {
},
methods: {
defaultUser: function () {
return {
name: '',
description: ''
}
},
createUser: function () {
// 入力パラメーターのバリデーション
if (this.user.name.trim() === '') {
this.error = 'Nameは必須です'
return
}
if (this.user.description.trim() === '') {
this.error = 'Descriptionは必須です'
return
}
postUser(this.user, (function (err, user) {
this.sending = false
if (err) {
this.error = err.toString()
} else {
this.error = null
// デフォルトでフォームをリセット
this.user = this.defaultUser()
alert('新規ユーザーが登録されました')
// ユーザー一覧ページに戻る
this.$router.push('/users')
}
}).bind(this))
}
}
}
// ルートオプションを渡してルーターインスタンスを生成
var router = new VueRouter({
// 各ルートにコンポーネントをマッピング
// コンポーネントはVue.extend() によって作られたコンポーネントコンストラクタでも
// コンポーネントオプションのオブジェクトでも渡せる
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
component: UserList
},
{
path: '/users/new',
component: UserCreate,
beforeEnter: function (to, from, next) {
// 認証されていない状態でアクセスした時はloginページに遷移する
if (!Auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
// 認証済みであればそのまま新規ユーザー作成ページへ進む
next()
}
}
},
{
// /users/newの前にこのルートを定義するとパターンマッチにより/users/newが動作しなくなるので注意
path: '/users/:userId',
component: UserDetail
},
{
path: '/login',
component: Login
},
{
path: '/logout',
beforeEnter: function (to, from, next) {
Auth.logout()
next('/top')
}
},
{
// 定義されていないパスへの対応。トップページへリダイレクトする。
path: '*',
redirect: '/top'
}
]
})
// ルーターのインスタンスをrootとなるVueインスタンスに渡す
var app = new Vue({
data: {
Auth: Auth
},
router: router
}).$mount('#app')
CSS
[v-cloak] {
display: none
}
まとめ
- なかなか難易度が高いですが、少しづつ改造をしていく中で理解したいと思います💧