Skip to content

ルーティングドキュメント - ルート設定と権限制御

概要

このドキュメントは、VJSP Vue3フレームワークのルーティング設定メカニズムと権限制御システムについて詳しく説明します。フレームワークはVue Router 4をベースとしたルーティングアーキテクチャを採用し、動的ルート生成と細かい権限制御を組み合わせ、エンタープライズレベルのアプリケーションに完全なルーティングソリューションを提供します。

ルーティングアーキテクチャ

コア機能

  • 動的ルート生成: フロントエンド制御とバックエンド制御の両方のルート生成モードをサポート
  • 権限統合: ルーティングと権限システムの深い統合、メニューレベルとボタンレベルの権限制御を実現
  • ルートキャッシュ: インテリジェントなルートキャッシュメカニズムでページ切り替えパフォーマンスを向上
  • マルチレベルルートサポート: マルチレベルネストルートをサポートし、自動的にフラット化
  • タイプセーフ: 完全なTypeScript型定義サポート

技術スタック

  • Vue Router 4: コアルーティングライブラリ
  • Pinia状態管理: ルート状態と権限状態の管理
  • 動的インポート: ルートコンポーネントの遅延読み込みで初期画面読み込みパフォーマンスを最適化
  • 権限検証: ロールベースのアクセス制御(RBAC)

ルート設定メカニズム

ルーティングモード

フレームワークは2つのルート設定モードをサポートします:

  1. フロントエンド制御モード: ルート設定は完全にフロントエンドで定義され、権限システムがアクセス可能なルートをフィルタリング
  2. バックエンド制御モード: ルート構造はバックエンドから返され、フロントエンドはバックエンドデータに基づいて動的にルートを生成

ルート構造

定数ルート

定数ルートはアプリケーション起動時に必ず読み込まれる基本ルートで、以下を含みます:

  • ログインページ
  • 404エラーページ
  • リダイレクトルート
  • システム基本ページ(ダッシュボードなど)

動的ルート

動的ルートはユーザー権限に基づいて動的に生成され、以下を含みます:

  • ビジネス機能モジュールルート
  • システム管理ルート
  • 個人センタールート

ルート遅延読み込み

フレームワークは動的インポートを使用してルートコンポーネントの遅延読み込みを実装し、createSafeDynamicImport関数を通じてコンポーネント読み込みの安全性を確保します:

  • オンデマンドでコンポーネントを読み込み、初期バンドルサイズを削減
  • エラーバウンダリ処理で読み込み失敗がアプリケーションに影響しないように防止
  • コンポーネントの事前読み込みをサポートし、ユーザーエクスペリエンスを向上

権限制御メカニズム

権限レベル

フレームワークは3段階の権限制御を実装します:

  1. ルートレベル権限: ユーザーが特定のページにアクセスできるかどうかを制御
  2. メニューレベル権限: サイドバーメニューの表示を制御
  3. ボタンレベル権限: ページ内の操作ボタンの表示を制御

権限検証プロセス

1. ルートガード検証

ルートガードは各ルートナビゲーション中に権限検証を実行します:

  • ユーザーログイン状態を確認
  • ユーザー権限情報を検証
  • アクセス可能な動的ルートを生成
  • ルーターインスタンスにルートを追加

2. 権限チェック関数

フレームワークは様々な権限チェック関数を提供します:

  • hasPermission: 特定の権限があるかどうかをチェック
  • hasAnyPermission: 指定された権限のいずれかがあるかどうかをチェック
  • hasAllPermissions: 指定されたすべての権限があるかどうかをチェック

3. 権限ディレクティブ

ボタンレベルの権限制御はv-permissionディレクティブを通じて実装され、様々な使用パターンと厳格な権限モードをサポートします:

基本使用法
vue
<!-- 単一権限チェック -->
<el-button v-permission="'system:user:add'">ユーザー追加</el-button>
<el-button v-permission="'system:user:edit'">ユーザー編集</el-button>

<!-- 複数権限チェック(いずれかの権限を満たす) -->
<el-button v-permission="['system:user:add', 'system:user:edit']">
  ユーザー追加または編集
</el-button>
厳格な権限モード

厳格な権限モードは修飾子を通じて実装され、ユーザーに権限がない場合にDOM要素を完全に削除します:

vue
<!-- 厳格モード:権限がない場合に要素を削除 -->
<el-button v-permission.strict="'system:user:delete'">ユーザー削除</el-button>

<!-- 複数権限での厳格モード -->
<el-button v-permission.strict="['system:user:export', 'system:user:import']">
  データインポート/エクスポート
</el-button>
権限ディレクティブの特徴
  • デフォルト動作: 権限がない場合に要素を無効化(透明度を下げ、クリック不可)
  • 厳格モード: .strict修飾子を使用して権限がない場合に要素を完全に削除
  • 動的更新: 権限が変更されたときに要素状態を自動更新
  • 複数権限チェック: 単一権限または権限配列をサポート(いずれかの権限を満たす)

動的ルート生成

フロントエンド制御モード

フロントエンド制御モードでは、ルート生成プロセスは以下の通りです:

  1. ユーザー権限リストを取得
  2. 事前定義されたルート設定をフィルタリング
  3. アクセス可能なルートツリーを生成
  4. ルーターインスタンスに追加

バックエンド制御モード

バックエンド制御モードでは、ルート生成プロセスは以下の通りです:

  1. バックエンドからメニューデータを取得
  2. メニューデータをルート設定に変換
  3. ルートコンポーネントパスの有効性を検証
  4. 動的ルートを生成して追加

ルートユーティリティ関数

ルート生成ツール

フレームワークはgenerateRoutesByFrontEndgenerateRoutesByServer関数を提供し、それぞれフロントエンドとバックエンド制御モードのルート生成を処理します。

ルート処理ツール

  • pathResolve: パス解決と正規化
  • flatMultiLevelRoutes: マルチレベルルートのフラット化
  • isValidRoute: ルート設定の検証

権限チェックツール

  • filterAccessibleRoutes: アクセス可能なルートをフィルタリング
  • findRouteByPath: パスでルートを検索
  • findRouteByName: 名前でルートを検索

ビジネスモジュール開発ガイド(製品モジュール)

1. ルート設定の作成

src/router/modules/ディレクトリにproduct.tsルート設定ファイルを作成:

typescript
import type { AppRouteRecordRaw } from '@/router/types'

const ProductRoute: AppRouteRecordRaw = {
  path: '/product',
  name: 'Product',
  component: () => import('@/layout/index.vue'),
  meta: {
    title: '製品管理',
    icon: 'product',
    permission: ['product:view'],
  },
  children: [
    {
      path: 'list',
      name: 'ProductList',
      component: () => import('@/views/modules/product/list.vue'),
      meta: {
        title: '製品一覧',
        permission: ['product:list'],
      },
    },
    {
      path: 'add',
      name: 'ProductAdd',
      component: () => import('@/views/modules/product/add.vue'),
      meta: {
        title: '製品追加',
        permission: ['product:add'],
        hidden: true,
      },
    },
    {
      path: 'edit/:id',
      name: 'ProductEdit',
      component: () => import('@/views/modules/product/edit.vue'),
      meta: {
        title: '製品編集',
        permission: ['product:edit'],
        hidden: true,
      },
    },
  ],
}

export default ProductRoute

2. ルートモジュールの登録

src/router/index.tsで製品ルートモジュールをインポートして登録:

typescript
import ProductRoute from './modules/product'

// 動的ルート設定に追加
const asyncRoutes: AppRouteRecordRaw[] = [
  // ... 他のルート
  ProductRoute,
]

3. ビューコンポーネントの作成

対応するVueコンポーネントファイルを作成:

src/views/modules/product/list.vue

vue
<template>
  <div class="product-list">
    <SearchForm :fields="searchFields" @search="handleSearch" />

    <DataTable
      :columns="tableColumns"
      :data="productList"
      :loading="loading"
      @refresh="loadProductList"
    >
      <template #action="{ row }">
        <el-button v-permission="'product:edit'" @click="handleEdit(row)"> 編集 </el-button>
        <el-button v-permission="'product:delete'" @click="handleDelete(row)"> 削除 </el-button>
      </template>
    </DataTable>

    <el-button v-permission="'product:add'" type="primary" @click="handleAdd"> 製品追加 </el-button>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useProductApi } from '@/api/modules/product'

const router = useRouter()
const productList = ref([])
const loading = ref(false)

const searchFields = [
  { label: '製品名', prop: 'productName', type: 'input' },
  { label: '製品コード', prop: 'productCode', type: 'input' },
]

const tableColumns = [
  { label: '製品名', prop: 'productName' },
  { label: '製品コード', prop: 'productCode' },
  { label: '価格', prop: 'price' },
  { label: 'ステータス', prop: 'status' },
]

const loadProductList = async (params = {}) => {
  loading.value = true
  try {
    const response = await useProductApi().getList(params)
    productList.value = response.data.list
  } finally {
    loading.value = false
  }
}

const handleAdd = () => {
  router.push({ name: 'ProductAdd' })
}

const handleEdit = row => {
  router.push({ name: 'ProductEdit', params: { id: row.id } })
}

const handleDelete = async row => {
  // 削除確認と操作
}

onMounted(() => {
  loadProductList()
})
</script>

4. 権限設定

権限管理システムで製品モジュールの権限識別子を設定:

  • product:view: 製品モジュールを表示
  • product:list: 製品一覧を表示
  • product:add: 製品を追加
  • product:edit: 製品を編集
  • product:delete: 製品を削除

ルートキャッシュメカニズム

ルート状態管理

フレームワークはPiniaを使用してルート状態を管理し、以下を含みます:

  • 現在アクティブなルート
  • タブルートリスト
  • サイドバーメニュー状態
  • ルートキャッシュ識別子

コンポーネントキャッシュ

ルートコンポーネントのキャッシュはVue Routerの<keep-alive>とコンポーネントのname属性を通じて実装されます:

  • インテリジェントなキャッシュ管理でメモリリークを回避
  • 最大キャッシュ数の制限をサポート
  • 手動キャッシュクリア機能

ルート永続化

ルート状態は自動的にlocalStorageに永続化され、以下を確保します:

  • ページリフレッシュ後もルート状態が維持
  • タブ状態の復元
  • サイドバー展開状態の記憶

エラー処理

ルートエラー処理

フレームワークは完全なルートエラー処理メカニズムを実装します:

  • 404ページ処理
  • 権限不足ページ
  • ルート読み込み失敗処理
  • ネットワークエラー再試行メカニズム

フォールバックソリューション

動的ルート読み込みが失敗した場合、フレームワークはフォールバックソリューションを提供します:

  • 静的ルートをバックアップとして使用
  • フレンドリーなエラーメッセージを表示
  • 再試行メカニズムを提供

ベストプラクティス

ルート命名規則

  • ルート名にはキャメルケースを使用
  • ルート名をコンポーネント名と一致させる
  • 意味のあるパス構造を使用

権限設定の推奨事項

  • 権限識別子にはモジュール化された命名を使用
  • 適切な権限の粒度を維持
  • 未使用の権限を定期的にクリーンアップ

パフォーマンス最適化

  • 適切にルート遅延読み込みを使用
  • 過度なルートネストを避ける
  • 定期的にルート設定をチェック

よくある質問

Q: 新しいビジネスモジュールルートを追加するには?

A: src/router/modules/ディレクトリにルート設定ファイルを作成し、メインルートファイルでインポート/登録します。

Q: ボタンレベルの権限を制御するには?

A: v-permissionディレクティブに対応する権限識別子を使用します。

Q: ルートキャッシュが機能しない場合、どうすればいいですか?

A: コンポーネントに正しいname属性が設定されていることを確認し、ルート設定のmeta.keepAlive設定をチェックします。

Q: 動的メニューを実装するには?

A: バックエンド制御モードを使用し、バックエンドからメニューデータを取得してルート設定に変換します。