Edited at

Vue.js入門 #07 4章 Vue Routerによるシングルページアプリケーション(SPA)

More than 1 year has passed since last update.

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')


  • それぞれのリンクをクリックすると表示が切り替わることを確認。

image.png

image.png


実践的なルーティングのための機能


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')

image.png


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(新規ログインなし) ログイン後↓

  • /usrs/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')


  • ユーザー一覧の画面が出力される。

image.png


詳細ページの実装


  • ユーザー詳細のテンプレートを追加する。

<!-- ユーザー詳細ページのテンプレート -->

<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>


  • ログイン画面

image.png


  • 新規ユーザー作成画面

image.png


  • 登録されたことを確認

image.png


サンプルアプリケーションの全体像


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
}


まとめ


  • なかなか難易度が高いですが、少しづつ改造をしていく中で理解したいと思います💧

参考

Vue.js入門 基礎から実践アプリケーション開発まで