Claude Code Plugins

Community-maintained marketplace

Feedback
4
0

Nuxt 4プロジェクトで再利用可能なComposableを作成する際のテンプレートとガイドライン

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name create-composable
description Nuxt 4プロジェクトで再利用可能なComposableを作成する際のテンプレートとガイドライン

Composable 作成スキル

このスキルは、「いぬいのうた」プロジェクトで再利用可能なComposition API ロジックを作成する際の標準パターンを提供します。

基本テンプレート

// composables/useSomething.ts
export const useSomething = () => {
  const state = ref<Type>(initialValue);
  const loading = ref(false);
  const error = ref<string | null>(null);

  const fetchData = async () => {
    loading.value = true;
    error.value = null;
    
    try {
      // ロジック実装
      state.value = result;
    } catch (e) {
      error.value = 'エラーメッセージ';
      console.error(e);
    } finally {
      loading.value = false;
    }
  };

  return {
    state,
    loading,
    error,
    fetchData,
  };
};

Composable の種類と用途

1. API通信用 Composable

// composables/useSongs.ts
export const useSongs = () => {
  const songs = ref<Song[]>([]);
  const loading = ref(false);
  const error = ref<string | null>(null);

  const fetchSongs = async (params?: SongSearchParams) => {
    loading.value = true;
    error.value = null;
    
    try {
      const data = await $fetch('/api/songs', { query: params });
      songs.value = data.results;
    } catch (e) {
      error.value = 'データの取得に失敗しました';
      console.error(e);
    } finally {
      loading.value = false;
    }
  };

  return { songs, loading, error, fetchSongs };
};

2. 状態管理用 Composable

// composables/useToggle.ts
export const useToggle = (initialValue = false) => {
  const state = ref(initialValue);

  const toggle = () => {
    state.value = !state.value;
  };

  const setTrue = () => {
    state.value = true;
  };

  const setFalse = () => {
    state.value = false;
  };

  return {
    state,
    toggle,
    setTrue,
    setFalse,
  };
};

3. ビジネスロジック用 Composable

// composables/usePlaybackControl.ts
export const usePlaybackControl = () => {
  const currentTime = ref(0);
  const duration = ref(0);
  const isPlaying = ref(false);

  const play = () => {
    isPlaying.value = true;
  };

  const pause = () => {
    isPlaying.value = false;
  };

  const seek = (time: number) => {
    currentTime.value = Math.max(0, Math.min(time, duration.value));
  };

  const progress = computed(() => {
    return duration.value > 0 ? currentTime.value / duration.value : 0;
  });

  return {
    currentTime,
    duration,
    isPlaying,
    progress,
    play,
    pause,
    seek,
  };
};

作成手順

1. ファイル配置

  • 場所: app/composables/
  • 命名規則: use プレフィックス + PascalCase
  • : useSongs.ts, usePlayer.ts, usePlaylist.ts

2. 型定義の作成

// composables/useSongs.ts
import type { Song, SongSearchParams } from '~/types/api';

export const useSongs = () => {
  // 実装
};

3. 戻り値の型を明示

interface UseSongsReturn {
  songs: Ref<Song[]>;
  loading: Ref<boolean>;
  error: Ref<string | null>;
  fetchSongs: (params?: SongSearchParams) => Promise<void>;
}

export const useSongs = (): UseSongsReturn => {
  // 実装
  return { songs, loading, error, fetchSongs };
};

4. 副作用の適切な管理

export const useAutoFetch = (url: string) => {
  const data = ref(null);
  
  // ライフサイクルフックの使用
  onMounted(async () => {
    data.value = await $fetch(url);
  });
  
  // クリーンアップ
  onUnmounted(() => {
    // 必要に応じてクリーンアップ
  });
  
  return { data };
};

重要なルール

必須事項

use プレフィックス

  • すべてのComposableは use で始める

型安全性

  • 戻り値の型を明示
  • ジェネリクスの活用

再利用性

  • 特定のコンポーネントに依存しない
  • パラメータで挙動をカスタマイズ可能

エラーハンドリング

  • 非同期処理は必ずtry-catchで囲む
  • エラー状態を返す

Composables vs Stores の使い分け

Composables を使うべき場合:

  • コンポーネントローカルな状態
  • API通信ロジック
  • 再利用可能なユーティリティ

Stores を使うべき場合:

  • アプリケーション全体で共有する状態
  • 複数コンポーネント間での同期が必要
  • 永続化が必要な状態

実装パターン集

パターン1: ページネーション付きAPI取得

export const usePaginatedFetch = <T>(endpoint: string) => {
  const items = ref<T[]>([]);
  const page = ref(1);
  const totalPages = ref(1);
  const loading = ref(false);

  const fetchPage = async (pageNum: number) => {
    loading.value = true;
    try {
      const data = await $fetch(endpoint, {
        query: { page: pageNum }
      });
      items.value = data.results;
      totalPages.value = data.total_pages;
      page.value = pageNum;
    } finally {
      loading.value = false;
    }
  };

  return { items, page, totalPages, loading, fetchPage };
};

パターン2: デバウンス検索

export const useDebouncedSearch = (searchFn: (query: string) => Promise<void>) => {
  const query = ref('');
  const loading = ref(false);
  let timeoutId: NodeJS.Timeout;

  const search = (value: string) => {
    query.value = value;
    loading.value = true;
    
    clearTimeout(timeoutId);
    timeoutId = setTimeout(async () => {
      await searchFn(value);
      loading.value = false;
    }, 300);
  };

  onUnmounted(() => {
    clearTimeout(timeoutId);
  });

  return { query, loading, search };
};

パターン3: ローカルストレージ連携

export const useLocalStorage = <T>(key: string, initialValue: T) => {
  const state = ref<T>(initialValue);

  // 初期化時にローカルストレージから読み込み
  onMounted(() => {
    const stored = localStorage.getItem(key);
    if (stored) {
      state.value = JSON.parse(stored);
    }
  });

  // 状態が変わったら保存
  watch(state, (newValue) => {
    localStorage.setItem(key, JSON.stringify(newValue));
  }, { deep: true });

  return state;
};

チェックリスト

Composable作成完了時に確認:

  • ファイル名は use プレフィックス
  • app/composables/ に配置
  • 戻り値の型を明示
  • エラーハンドリング実装
  • 副作用を適切に管理(onMounted, onUnmounted等)
  • 再利用性を考慮した設計
  • TypeScript strictモード準拠
  • 必要に応じてジェネリクス活用