Skip to content

VJSP Vue3 Frame - ユーティリティ関数ガイド

概要

VJSP Vue3 Frameは、キャッシュ管理、セキュリティ保護、ルーティングと権限、メッセージ管理など、フロントエンド開発の様々な側面をサポートする包括的なユーティリティ関数ライブラリを提供します。このガイドでは、ユーティリティ関数のアーキテクチャ、使用方法、ベストプラクティスについて詳しく説明します。

アーキテクチャ設計

1. ディレクトリ構造

utils/
├── cache/                    # キャッシュ管理層
│   ├── CacheUtil.ts         # キャッシュユーティリティクラス
│   ├── MemoryCache.ts       # メモリキャッシュ実装
│   └── StorageCache.ts      # 永続ストレージキャッシュ
├── security/                # セキュリティ保護層
│   ├── SecurityManager.ts    # セキュリティマネージャー
│   ├── RateLimiter.ts       # レート制限
│   └── CSRFProtection.ts    # CSRF保護
├── router/                  # ルーティングと権限
│   ├── RouterHelper.ts      # ルーティングヘルパー
│   ├── PermissionUtil.ts    # 権限ユーティリティ
│   └── RouteGuard.ts        # ルートガード
├── message/                # メッセージ管理
│   ├── MessageManager.ts    # メッセージマネージャー
│   └── NotificationUtil.ts  # 通知ユーティリティ
├── common/                 # 共通ユーティリティ
│   ├── DateUtil.ts         # 日付ユーティリティ
│   ├── StringUtil.ts       # 文字列ユーティリティ
│   └── ObjectUtil.ts       # オブジェクトユーティリティ
└── index.ts               # 統一エクスポート

2. 設計原則

単一責任の原則: 各ユーティリティ関数は特定の機能に焦点を当てる

モジュール設計: 明確なインターフェースを持つ独立したモジュール

型安全性: 完全なTypeScriptサポートと完全な型定義

パフォーマンス最適化: 最小限のオーバーヘッドで効率的な実装

セキュリティ保護: 組み込みのセキュリティメカニズム

コアユーティリティメカニズム

1. キャッシュ管理

メモリキャッシュ機能:

  • メモリ内データストレージ
  • 自動有効期限管理
  • メモリリーク防止
  • パフォーマンス監視

ストレージキャッシュ機能:

  • LocalStorage/SessionStorageサポート
  • データシリアライズ/デシリアライズ
  • ストレージクォータ管理
  • クロスタブ同期

2. セキュリティ保護

レート制限メカニズム:

  • マルチレベルレート制限(グローバル/ユーザー/エンドポイント)
  • スライディングウィンドウアルゴリズム
  • 動的設定調整
  • リアルタイム監視

CSRF保護:

  • トークン生成と検証
  • ダブルサブミットクッキーパターン
  • 自動トークン更新
  • リクエストヘッダー保護

3. ルーティングと権限

ルートガード機能:

  • 権限検証
  • ルートリダイレクト
  • ログイン状態チェック
  • ルートキャッシュ管理

権限ユーティリティ機能:

  • 権限チェック
  • 権限フィルタリング
  • 動的権限ロード
  • 権限レベル管理

4. メッセージ管理

メッセージタイプ:

  • 成功メッセージ
  • 警告メッセージ
  • エラーメッセージ
  • 情報メッセージ

通知機能:

  • 自動非表示
  • 位置設定
  • カスタム期間
  • スタック管理

製品モジュール開発例

1. 製品データキャッシュ実装

typescript
// src/views/product/ProductList.vue
<script setup lang="ts">
import { CacheUtil } from '@/utils/cache'
import { message } from '@/utils/messageManager'

// キャッシュ設定
const PRODUCT_CACHE_KEY = 'product_list_cache'
const CACHE_EXPIRY = 5 * 60 * 1000 // 5分

// 製品リストデータ
const productList = ref([])
const loading = ref(false)

// キャッシュを使用した製品データの読み込み
const loadProductList = async () => {
  try {
    loading.value = true

    // まずキャッシュを確認
    const cachedData = CacheUtil.get(PRODUCT_CACHE_KEY)
    if (cachedData && !CacheUtil.isExpired(PRODUCT_CACHE_KEY)) {
      productList.value = cachedData
      console.log('キャッシュから読み込み')
      return
    }

    // APIから取得
    const response = await productApi.getList()
    productList.value = response.data

    // キャッシュを更新
    CacheUtil.set(PRODUCT_CACHE_KEY, productList.value, CACHE_EXPIRY)
    console.log('APIから読み込み、キャッシュに保存')

  } catch (error) {
    message.error('製品リストの読み込みに失敗しました')
    console.error('製品読み込みエラー:', error)
  } finally {
    loading.value = false
  }
}

// 製品キャッシュのクリア
const clearProductCache = () => {
  CacheUtil.remove(PRODUCT_CACHE_KEY)
  message.success('製品キャッシュをクリアしました')
}

// 製品データの更新
const refreshProductList = () => {
  CacheUtil.remove(PRODUCT_CACHE_KEY)
  loadProductList()
}

// コンポーネントマウント時に初期化
onMounted(() => {
  loadProductList()
})
</script>

<template>
  <div class="product-list">
    <div class="header">
      <h2>製品管理</h2>
      <div class="actions">
        <el-button @click="refreshProductList" :loading="loading">
          更新
        </el-button>
        <el-button @click="clearProductCache" type="warning">
          キャッシュクリア
        </el-button>
      </div>
    </div>

    <el-table :data="productList" v-loading="loading">
      <!-- テーブル列 -->
    </el-table>
  </div>
</template>

2. 製品権限制御実装

typescript
// src/views/product/components/ProductActions.vue
<script setup lang="ts">
import { hasPermission, filterByPermission } from '@/utils/permission'

// 製品操作権限定義
const PRODUCT_PERMISSIONS = {
  CREATE: 'product:create',
  EDIT: 'product:edit',
  DELETE: 'product:delete',
  VIEW: 'product:view'
}

// 現在のユーザー権限を確認
const canCreateProduct = hasPermission(PRODUCT_PERMISSIONS.CREATE)
const canEditProduct = hasPermission(PRODUCT_PERMISSIONS.EDIT)
const canDeleteProduct = hasPermission(PRODUCT_PERMISSIONS.DELETE)

// 製品操作ボタン設定
const actionButtons = [
  {
    label: '製品作成',
    icon: 'plus',
    permission: PRODUCT_PERMISSIONS.CREATE,
    handler: () => createProduct()
  },
  {
    label: '製品編集',
    icon: 'edit',
    permission: PRODUCT_PERMISSIONS.EDIT,
    handler: () => editProduct()
  },
  {
    label: '製品削除',
    icon: 'delete',
    permission: PRODUCT_PERMISSIONS.DELETE,
    handler: () => deleteProduct()
  }
]

// 権限に基づいて操作ボタンをフィルタリング
const visibleButtons = filterByPermission(actionButtons, 'permission')

// 製品操作関数
const createProduct = () => {
  if (!hasPermission(PRODUCT_PERMISSIONS.CREATE)) {
    message.warning('製品作成の権限がありません')
    return
  }
  // 製品作成ロジック
}

const editProduct = () => {
  if (!hasPermission(PRODUCT_PERMISSIONS.EDIT)) {
    message.warning('製品編集の権限がありません')
    return
  }
  // 製品編集ロジック
}

const deleteProduct = () => {
  if (!hasPermission(PRODUCT_PERMISSIONS.DELETE)) {
    message.warning('製品削除の権限がありません')
    return
  }
  // 製品削除ロジック
}
</script>

<template>
  <div class="product-actions">
    <el-button
      v-for="button in visibleButtons"
      :key="button.label"
      :icon="button.icon"
      @click="button.handler"
    >
      {{ button.label }}
    </el-button>
  </div>
</template>

3. 製品ルート設定とキャッシュ

typescript
// src/router/modules/product.ts
import { Layout } from '@/utils/routerHelper'
import type { AppRouteRecordRaw } from '@/types/router'

const ProductRoute: AppRouteRecordRaw = {
  path: '/product',
  component: Layout,
  redirect: '/product/list',
  name: 'Product',
  meta: {
    title: '製品管理',
    icon: 'shopping',
    permission: 'product',
    keepAliveName: 'ProductLayout',
  },
  children: [
    {
      path: 'list',
      component: () => import('@/views/product/ProductList.vue'),
      name: 'ProductList',
      meta: {
        title: '製品リスト',
        icon: 'list',
        permission: 'product:view',
        keepAliveName: 'ProductList',
        noCache: false, // キャッシュを有効化
      },
    },
    {
      path: 'detail/:id',
      component: () => import('@/views/product/ProductDetail.vue'),
      name: 'ProductDetail',
      meta: {
        title: '製品詳細',
        icon: 'detail',
        permission: 'product:view',
        keepAliveName: 'ProductDetail',
        noCache: true, // キャッシュを無効化(詳細ページは通常キャッシュ不要)
      },
      props: true,
    },
    {
      path: 'create',
      component: () => import('@/views/product/ProductCreate.vue'),
      name: 'ProductCreate',
      meta: {
        title: '製品作成',
        icon: 'plus',
        permission: 'product:create',
        noCache: true, // 作成ページはキャッシュ不要
      },
    },
  ],
}

export default ProductRoute

4. 製品APIセキュリティ保護

typescript
// src/api/product.ts
import { securityManager } from '@/utils/securityManager'
import { message } from '@/utils/messageManager'

class ProductApi {
  private baseURL = '/api/product'

  // 製品リストの取得(レート制限付き)
  async getList(params?: any) {
    // レート制限を確認
    const userId = this.getCurrentUserId()
    const endpoint = `${this.baseURL}/list`

    if (!securityManager.isRequestAllowed(userId, endpoint)) {
      message.warning('リクエストが頻繁すぎます。しばらくしてから再試行してください')
      throw new Error('レート制限超過')
    }

    // CSRF保護ヘッダーを追加
    const headers = securityManager.getCSRFHeaders('GET')

    return await axios.get(endpoint, {
      params,
      headers,
    })
  }

  // 製品作成(セキュリティ検証付き)
  async create(productData: any) {
    const userId = this.getCurrentUserId()
    const endpoint = `${this.baseURL}/create`

    // レート制限を確認
    if (!securityManager.isRequestAllowed(userId, endpoint)) {
      message.warning('操作が頻繁すぎます。しばらくしてから再試行してください')
      throw new Error('レート制限超過')
    }

    // CSRF保護ヘッダーを追加
    const headers = securityManager.getCSRFHeaders('POST')

    return await axios.post(endpoint, productData, { headers })
  }

  // 製品削除(権限検証付き)
  async delete(productId: string) {
    const userId = this.getCurrentUserId()
    const endpoint = `${this.baseURL}/delete/${productId}`

    // レート制限を確認
    if (!securityManager.isRequestAllowed(userId, endpoint)) {
      message.warning('操作が頻繁すぎます。しばらくしてから再試行してください')
      throw new Error('レート制限超過')
    }

    // CSRF保護ヘッダーを追加
    const headers = securityManager.getCSRFHeaders('DELETE')

    return await axios.delete(endpoint, { headers })
  }

  private getCurrentUserId(): string {
    // 現在のユーザーIDを取得するロジック
    const userStore = useUserStore()
    return userStore.getUserInfo?.id || 'anonymous'
  }
}

export const productApi = new ProductApi()

パフォーマンス最適化実践

1. キャッシュ戦略の最適化

製品リストキャッシュの最適化:

  • メモリキャッシュ+永続キャッシュの二重メカニズムを使用
  • データ更新頻度に基づいて適切な有効期限を設定
  • キャッシュプリロードメカニズムを実装し、ユーザーエクスペリエンスを向上

2. コンポーネントキャッシュ設定

keep-aliveキャッシュ戦略:

vue
<!-- src/layout/components/AppMain.vue -->
<template>
  <section class="app-main">
    <router-view v-slot="{ Component }">
      <transition name="fade-transform" mode="out-in">
        <keep-alive :include="cachedViews">
          <component :is="Component" :key="route.path" />
        </keep-alive>
      </transition>
    </router-view>
  </section>
</template>

<script setup lang="ts">
import { useTagsViewStore } from '@/layout/stores/tagsView'

const route = useRoute()
const tagsViewStore = useTagsViewStore()

// キャッシュされたビューリストを取得
const cachedViews = computed(() => tagsViewStore.getCachedViews)
</script>

3. リクエスト最適化戦略

製品APIリクエストの最適化:

  • リクエストの重複排除を実装し、重複リクエストを回避
  • リクエストキャッシュを使用してサーバー負荷を軽減
  • リクエスト優先度管理を実装し、重要なリクエストを優先処理

ベストプラクティスガイド

1. ユーティリティ関数使用基準

インポート基準:

typescript
// 推奨:必要に応じてインポート
import { CacheUtil } from '@/utils/cache'
import { hasPermission } from '@/utils/permission'

// 非推奨:グローバルインポート(バンドルサイズが増加)
import * as utils from '@/utils'

エラーハンドリング基準:

typescript
// try-catchを使用してユーティリティ関数の例外を処理
try {
  const userInfo = SecurityStorage.getUserInfo()
} catch (error) {
  console.error('ユーザー情報の取得に失敗しました:', error)
  // フォールバック処理またはユーザー通知
}

2. キャッシュ使用推奨事項

キャッシュに適したデータ:

  • 静的設定データ
  • 基本ユーザー情報
  • 製品カテゴリデータ
  • 権限メニューデータ

キャッシュに適さないデータ:

  • リアルタイム性が要求されるデータ
  • 頻繁に更新されるビジネスデータ
  • 機密性の高い取引データ

3. セキュリティ保護設定

本番環境設定:

typescript
// 厳格なセキュリティ保護を有効化
const securityConfig = {
  rateLimit: {
    enabled: true,
    global: { maxRequests: 100, timeWindow: 60000 },
    user: { maxRequests: 10, timeWindow: 60000 },
    endpoint: { maxRequests: 5, timeWindow: 60000 },
  },
  csrf: {
    enabled: true,
    tokenExpiry: 30 * 60 * 1000, // 30分
    doubleSubmit: true,
  },
}

まとめ

VJSP Vue3 Frameのユーティリティ関数ライブラリは、プロジェクト開発に強力な基盤サポートを提供します。合理的なモジュール分割、包括的なメカニズム設計、パフォーマンス最適化戦略を通じて、ユーティリティ関数ライブラリは開発効率とコード品質を効果的に向上させることができます。実際の開発では、特定のビジネス要件に基づいて適切なユーティリティ関数を選択し、ベストプラクティス基準に従うことを推奨します。