
Vue Router ナビゲーションガード認証とルート保護
2025-07-31はじめに
Vue.jsのルーティングシステムであるVue Routerには、ナビゲーションを制御する強力な機能「ナビゲーションガード」が備わっています。この記事では、ナビゲーションガードの基本から実践的な活用方法まで詳細な解説を行います。
ナビゲーションガードとは?
ナビゲーションガードは、Vue Routerが提供するナビゲーション(ルート遷移)をフックして制御する仕組みです。主に以下のような場面で使用します:
- ユーザー認証の確認
- ルートアクセス権限のチェック
- 未保存データの確認
- ページ遷移前のデータ取得
- 分析トラッキングの実施

ナビゲーションガードの種類
Vue Routerには3つの主要なナビゲーションガードがあります:
- グローバルガード – すべてのナビゲーションで実行
- ルート単位ガード – 特定のルートに対して実行
- コンポーネント内ガード – コンポーネント内で実行
1. グローバルガード
グローバルガードはルーターインスタンスに直接登録され、すべてのナビゲーションで実行されます。
router.beforeEach
– グローバル前置ガード
const router = createRouter({ ... })
router.beforeEach((to, from, next) => {
// to: 遷移先のルートオブジェクト
// from: 遷移元のルートオブジェクト
// next: ナビゲーションを解決する関数
if (to.meta.requiresAuth && !isAuthenticated()) {
next('/login') // ログインページにリダイレクト
} else {
next() // ナビゲーションを許可
}
})
router.beforeResolve
– グローバル解決ガード
beforeEach
に似ていますが、ナビゲーションが解決される直前、コンポーネントガードと非同期コンポーネントが解決された後に呼び出されます。
router.beforeResolve(async to => {
if (to.meta.requiresFetch) {
await fetchData(to.params.id)
}
})
router.afterEach
– グローバル後置ガード
ナビゲーションが完了した後に実行されます。next
関数は受け取りません。
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
2. ルート単位ガード
特定のルートに対して直接定義できるガードです。
const routes = [
{
path: '/admin',
component: AdminPanel,
beforeEnter: (to, from, next) => {
if (!isAdmin()) {
next('/access-denied')
} else {
next()
}
}
}
]
3. コンポーネント内ガード
ルートコンポーネント内で直接定義できるガードです。
beforeRouteEnter
export default {
beforeRouteEnter(to, from, next) {
// コンポーネントインスタンスはまだ作成されていない
next(vm => {
// vmを通じてコンポーネントインスタンスにアクセス
console.log(vm.someProperty)
})
}
}
beforeRouteUpdate
export default {
beforeRouteUpdate(to, from, next) {
// 同じコンポーネントでルートが変更された時
// 例: /users/1 → /users/2
this.userData = fetchUser(to.params.id)
next()
}
}
beforeRouteLeave
export default {
beforeRouteLeave(to, from, next) {
if (this.unsavedChanges) {
if (confirm('変更が保存されていません。離れますか?')) {
next()
} else {
next(false) // ナビゲーションを中止
}
} else {
next()
}
}
}
実践的な使用例
認証システムの実装
// 認証状態をチェックする関数
const isAuthenticated = () => {
return localStorage.getItem('authToken') !== null
}
// 管理者権限をチェックする関数
const isAdmin = () => {
const user = JSON.parse(localStorage.getItem('user'))
return user && user.role === 'admin'
}
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
// 認証が必要なページかつ未認証の場合
next({
path: '/login',
query: { redirect: to.fullPath } // ログイン後にリダイレクト
})
} else if (to.meta.adminOnly && !isAdmin()) {
// 管理者限定ページかつ管理者でない場合
next('/forbidden')
} else {
next()
}
})
ルートメタフィールドの活用
const routes = [
{
path: '/',
component: Home,
meta: { requiresAuth: false }
},
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true }
},
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true, adminOnly: true }
}
]
ページタイトルの動的設定
router.beforeEach((to, from, next) => {
document.title = to.meta.title || 'デフォルトタイトル'
next()
})
ナビゲーションガードの実行順序
ナビゲーションガードは以下の順序で実行されます:
- ナビゲーションがトリガーされる
- 非アクティブ化されるコンポーネントで
beforeRouteLeave
を呼び出し - グローバル
beforeEach
ガードを呼び出し - 再利用されるコンポーネントで
beforeRouteUpdate
を呼び出し - ルート設定内の
beforeEnter
を呼び出し - 非同期ルートコンポーネントを解決
- アクティブ化されるコンポーネントで
beforeRouteEnter
を呼び出し - グローバル
beforeResolve
ガードを呼び出し - ナビゲーションが確定
- グローバル
afterEach
フックを呼び出し - DOM更新がトリガーされる
beforeRouteEnter
ガードで渡されたnextコールバックを呼び出し
よくあるエラーとデバッグ方法
無限リダイレクトループ
router.beforeEach((to, from, next) => {
if (!isAuthenticated() && to.path !== '/login') {
next('/login') // ログインページへ
} else {
next() // 許可
}
})
このコードでは、/login
ページ自体にrequiresAuth
メタフィールドが設定されていると無限ループに陥る可能性があります。
解決策:
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated() && to.path !== '/login') {
next('/login')
} else {
next()
}
})
next()の呼び忘れ
ガード関数でnext()
を呼び忘れると、ナビゲーションがハングします。
router.beforeEach((to, from, next) => {
if (someCondition) {
next('/other-path')
}
// next()が呼ばれていない!
})
正しい実装:
router.beforeEach((to, from, next) => {
if (someCondition) {
next('/other-path')
} else {
next()
}
})
パフォーマンス最適化のヒント
- ガード内の処理を最小化: ガード内で重い処理を行わない
- メモ化: 繰り返し行われる認証チェックをキャッシュ
- 非同期処理の適切な管理: ガード内でAPI呼び出しが必要な場合はローディング状態を表示
- 不要なガードを削除: 本当に必要な場合のみガードを使用
テスト戦略
ナビゲーションガードのテスト例(Jestを使用):
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from '@/router'
import { isAuthenticated } from '@/auth'
jest.mock('@/auth')
describe('Navigation Guards', () => {
let router
beforeEach(() => {
router = createRouter({
history: createWebHistory(),
routes
})
})
it('should redirect to login when accessing protected route without auth', async () => {
isAuthenticated.mockReturnValue(false)
await router.push('/protected')
expect(router.currentRoute.value.path).toBe('/login')
})
it('should allow access to protected route with auth', async () => {
isAuthenticated.mockReturnValue(true)
await router.push('/protected')
expect(router.currentRoute.value.path).toBe('/protected')
})
})
まとめ
Vue Routerのナビゲーションガードは、アプリケーションのセキュリティとユーザー体験を向上させる強力なツールです。適切に実装することで:
- 認証状態に基づいてルートを保護
- 権限に応じたアクセス制御
- 未保存データの損失防止
- ナビゲーション前のデータ事前取得
- 分析トラッキングの実施
が可能になります。
ナビゲーションガードを効果的に使用するには、その実行順序と各ガードの特性を理解することが重要です。また、無限リダイレクトループなどの一般的な落とし穴を避けるため、慎重に実装する必要があります。
さらに学ぶために
ナビゲーションガードをマスターすることで、より安全で堅牢なVue.jsアプリケーションを構築できるようになります。実際のプロジェクトでこれらのテクニックを試しながら、理解を深めていきましょう。