Communitygithub.com

vue-patterns

Vue.js 3 Composition APIのパターン、コンポーネントアーキテクチャ、リアクティビティのベストプラクティス、Pinia状態管理、Vue Routerナビゲーション、Nuxt SSRパターンを網羅。Vue、Nuxt、Vite、Piniaプロジェクトで有効化。

vue-patterns とは?

このスキルは、Vue.js 3(Composition API、`<script setup>`)を使用した開発のための包括的なガイドです。コンポーネント設計、リアクティビティ、状態管理、ルーティング、テスト、SSRパターンをカバーし、Nuxt固有のガイダンスも含まれています。開発者がVue、Nuxt、Vite、Piniaを使用するプロジェクトで、アーキテクチャの設計やコードレビューを行う際に役立ちます。推奨されるプロジェクト構造、コンポーネントのベストプラクティス、Composableの作成ルール、Piniaストアのセットアップ方法、Vue Routerの設定、パフォーマンス最適化手法、テストパターンなどを詳細に説明しています。また、Vue 3.5+の新しいAPI(リアクティブなProps分割代入、`useTemplateRef()`、`onWatcherCleanup()`、`useId()`、`defer` Teleportなど)や、よくあるアンチパターンとその修正方法についても解説しています。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/vue-patterns

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Vue.js パターンとベストプラクティス

Composition API(<script setup>)を使用したVue.js 3開発のための包括的ガイド。コンポーネント設計、リアクティビティ、状態管理、ルーティング、テスト、SSRパターンをカバーします。Nuxt固有のガイダンスは、通常のVueと異なる部分に記載しています。

有効化するタイミング

以下の場合にこのスキルを有効化してください:

  • プロジェクトがVue.js(任意のバージョン)、Nuxt、Vite + Vue、またはPiniaを使用している場合。
  • ユーザーがVueコンポーネントアーキテクチャ、Composable、リアクティビティ、または状態管理について質問した場合。
  • Vue Single-File Components(.vueファイル)をレビューする場合。
  • Vue Router、Piniaストア、またはVite/Vitestの設定を行う場合。
  • Vue固有のパフォーマンス、セキュリティ、またはSSRパターンについて議論する場合。

1. プロジェクト構造

推奨レイアウト(フィーチャーファースト)

src/
├── api/              # APIクライアントとエンドポイント定義
├── assets/           # 静的アセット(画像、フォント、アイコン)
├── components/       # 共有/再利用可能コンポーネント
│   ├── base/         # 基本UIプリミティブ(Button、Input、Modal)
│   └── features/     # 機能固有の共有コンポーネント
├── composables/      # 再利用可能なComposition APIロジック
├── layouts/          # ページレイアウト(任意)
├── pages/            # ルートレベルのページコンポーネント
├── router/           # Vue Router設定
├── stores/           # Piniaストア
├── types/            # TypeScript型定義
├── utils/            # 純粋なユーティリティ関数
└── App.vue           # ルートコンポーネント

ファイル命名規則

規則使用タイミング
PascalCase.vueすべてのコンポーネント (vue/multi-word-component-namesで強制)
useCamelCase.tsComposable
camelCase.tsユーティリティ、APIクライアント、型定義
kebab-case ディレクトリルートセグメント、機能フォルダ

2. コンポーネントアーキテクチャ

Single-File Componentの順序

<script setup lang="ts">
// 1. インポート(vue → エコシステム → 絶対パス → 相対パス)
// 2. Props & Emits & Slots
// 3. Composable
// 4. ローカル状態 (ref/reactive)
// 5. 算出プロパティ
// 6. メソッド
// 7. ウォッチャー
// 8. ライフサイクルフック
</script>

<template>
  <!-- テンプレート内容 -->
</template>

<style scoped>
  /* スコープ付きスタイル */
</style>

プレゼンテーショナル vs コンテナ

  • コンテナコンポーネント: データ取得、状態、副作用を所有。プレゼンテーショナルコンポーネントをレンダリング。
  • プレゼンテーショナルコンポーネント: Propsを受け取り、イベントを発行。APIコールやストアアクセスは行わない。純粋なレンダリング。

Propsのベストプラクティス

// 型ベースのPropsとデフォルト値
interface Props {
  label: string;
  variant?: "primary" | "secondary";
  disabled?: boolean;
  items: Item[];
}

const props = withDefaults(defineProps<Props>(), {
  variant: "primary",
  disabled: false,
});
  • 常に type、および必要に応じて required/default を指定する。
  • 真偽値のProps: isXxx、hasXxx、canXxx。
  • Propsを直接変更しない — 代わりにイベントを発行する。
  • v-modelバインディングには、defineModel()(Vue 3.4+)または modelValue + update:modelValue を使用する。

イベント

const emit = defineEmits<{
  submit: [];
  "update:modelValue": [value: string];
  select: [id: string, index: number];
}>();
  • テンプレートではケバブケースを使用する(@update:model-value)。
  • スクリプト内ではキャメルケースを使用する(emit("update:modelValue", val))。

3. Composable(再利用可能なロジック)

構造

// composables/useDebounce.ts
export function useDebounce<T>(value: MaybeRef<T>, delay: number): Ref<T> {
  const debounced = ref(toValue(value)) as Ref<T>;

  let timer: ReturnType<typeof setTimeout>;
  watch(
    () => toValue(value),
    (newVal) => {
      clearTimeout(timer);
      timer = setTimeout(() => { debounced.value = newVal; }, delay);
    }
  );

  onUnmounted(() => clearTimeout(timer));
  return readonly(debounced);
}

ルール

  • 必ず use プレフィックスで始める。
  • リアクティブな値(ref、computed、reactive)を返し、プレーンなプリミティブは返さない。
  • MaybeRef / toRef() / toValue() を使用してリアクティブな入力を受け入れる。
  • onUnmounted またはウォッチャーの onCleanup で副作用をクリーンアップする。
  • モジュールスコープの副作用を持たせない。

Mixinsとの比較

ComposableはVue 2のmixinを完全に置き換えます:

  • Mixin: 不透明なデータフロー、情報源の競合、名前の衝突。
  • Composable: 明示的なインポート、明確な戻り値、合成可能でツリーシェイカブル。

4. 状態管理

いつ何を使うか

パターンユースケース
ref() / reactive()ローカルコンポーネントの状態
Props + Emits親子間通信
Provide / Injectテーマ、設定、プラグインAPI
Piniaストアグローバル、共有、複雑な状態
サーバー状態のComposableAPIデータのキャッシュ(fetch/TanStack Queryをラップ)

Pinia Setup Store(推奨)

// stores/useCartStore.ts
export const useCartStore = defineStore("cart", () => {
  const items = ref<CartItem[]>([]);
  const isLoading = ref(false);

  const totalPrice = computed(() =>
    items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)
  );
  const itemCount = computed(() =>
    items.value.reduce((sum, i) => sum + i.quantity, 0)
  );

  async function addItem(productId: string) {
    isLoading.value = true;
    try {
      const item = await fetchProduct(productId);
      const existing = items.value.find(i => i.id === item.id);
      if (existing) existing.quantity++;
      else items.value.push({ ...item, quantity: 1 });
    } finally {
      isLoading.value = false;
    }
  }

  return { items, isLoading, totalPrice, itemCount, addItem };
});
  • Setup Store構文(Options Storeではなく)を使用する。
  • ビジネスレベルの変更にはアクションを、グループ化された更新には $patch() を優先する。
  • すべての非同期アクション: ローディング + 成功 + エラーを処理する。

5. Vue Router

ルート定義

const routes = [
  {
    path: "/users/:id",
    name: "user-detail",
    component: () => import("@/pages/UserDetail.vue"), // 遅延ロード
    props: true, // paramsをpropsとして渡す
    meta: { requiresAuth: true },
  },
];

ナビゲーションガード

router.beforeEach((to, from) => {
  const { isLoggedIn } = useAuthStore();
  if (to.meta.requiresAuth && !isLoggedIn) {
    return { name: "login", query: { redirect: to.fullPath } };
  }
});

リアクティブルートパラメータ

コンポーネントがマウントされたままルートパラメータが変更される場合:

const route = useRoute();
const id = computed(() => route.params.id as string);
watch(id, (newId) => fetchItem(newId));

6. テンプレートパターン

テンプレート構文

<!-- v-if/v-else-if/v-else -->
<div v-if="isLoading">読み込み中...</div>
<div v-else-if="error">エラー: {{ error }}</div>
<div v-else>{{ content }}</div>

<!-- 頻繁な切り替えにはv-show -->
<div v-show="isOpen">切り替えられたコンテンツ</div>

<!-- 安定したキーを持つv-for -->
<div v-for="item in items" :key="item.id">{{ item.name }}</div>

<!-- 算出プロパティによるフィルタリング(同じ要素にv-if + v-forは使わない) -->
<div v-for="item in activeItems" :key="item.id">{{ item.name }}</div>

<!-- イベント処理 -->
<form @submit.prevent="handleSubmit">
  <button type="submit">保存</button>
</form>

<!-- v-model -->
<input v-model="name" />
<CustomInput v-model="value" v-model:title="title" />

7. パフォーマンス

テクニック使用タイミング
v-memoめったに変更されないリストアイテム
v-once一度レンダリングされ、その後静的なコンテンツ
shallowRef()全体が置き換えられる大きなデータ構造
shallowReactive()トップレベルのプロパティのみリアクティブにする場合
v-ifより v-show頻繁な表示/非表示の切り替え
<KeepAlive :max="10">切り替えられるビューをキャッシュ
遅延ルート非クリティカルルートには () => import(...)
Suspenseフォールバック付きの非同期コンポーネント読み込み

8. テスト

スタック

  • Vitest:ユニットテストとコンポーネントテスト
  • Vue Test Utils:マウンティングとインタラクション
  • @pinia/testing:ストアのモック
  • Playwright:E2Eテスト

コンポーネントテストパターン

import { mount } from "@vue/test-utils";
import { createPinia, setActivePinia } from "pinia";
import UserCard from "./UserCard.vue";

beforeEach(() => { setActivePinia(createPinia()); });

it("レンダリングと発行", async () => {
  const wrapper = mount(UserCard, {
    props: { user: { id: "1", name: "Alice" } },
  });
  expect(wrapper.text()).toContain("Alice");
  await wrapper.find("button").trigger("click");
  expect(wrapper.emitted("select")![0]).toEqual(["1"]);
});

9. Nuxt固有のパターン

自動インポート

Nuxtは ref、computed、watch、useFetch、useAsyncData などを自動インポートします。これらはインポートせずに直接使用します。Nuxt以外のプロジェクトでは、常に明示的にインポートしてください。

useAsyncData / useFetch

const { data: user, pending, error, refresh } = await useAsyncData(
  "user", // キャッシュ用の一意のキー
  () => $fetch(`/api/users/${id}`),
);

const { data: posts } = await useFetch("/api/posts", {
  query: { page: 1 },
  key: "posts-page-1", // リクエストの重複排除
});

サーバールート

// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
  const { id } = await getValidatedRouterParams(event, z.object({
    id: z.string().uuid(),
  }).parse);
  // ... データ取得と返却
});

ランタイム設定

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // サーバーサイドのみ
    apiSecret: "",
    // 公開設定(クライアントに公開)
    public: {
      apiBase: "https://api.example.com",
    },
  },
});

10. Vue 3.5+ の新しいAPI

リアクティブなProps分割代入

Vue 3.5ではリアクティブなProps分割代入が安定化されました。defineProps() から分割代入された変数は自動的にリアクティブになります:

// Vue 3.5+: 分割代入されたPropsはリアクティブ(toRefsは不要)
const { count = 0, msg = "hello" } = defineProps<{
  count?: number;
  msg?: string;
}>();

// 制限: 分割代入されたPropsを直接watchすることはできない
watch(() => count, (newVal) => { ... }); // PASS getterが必要

useTemplateRef()

テンプレート参照のために、名前付きのプレーンなrefを useTemplateRef() で置き換えます:

import { useTemplateRef } from "vue";
const inputEl = useTemplateRef<HTMLInputElement>("input");
// "input"はテンプレート内のref="input"属性と一致し、変数名ではない

動的なref IDもサポート:useTemplateRef(dynamicRefId)。

onWatcherCleanup()

グローバルにインポート可能なウォッチャークリーンアップAPI(Vue 3.5+)。ウォッチャーのコールバック内で同期的に呼び出す必要があります:

import { watch, onWatcherCleanup } from "vue";

watch(userId, async (newId) => {
  const controller = new AbortController();
  onWatcherCleanup(() => controller.abort());
  // ... signalを使用してデータ取得
});

useId()

フォーム要素とアクセシビリティのためのSSRで安定した一意のID生成:

import { useId } from "vue";
const id = useId();

defer Teleport

<Teleport defer> を使用すると、同じレンダリングサイクル内でレンダリングされたターゲットにテレポートできます:

<Teleport defer to="#container">コンテンツ</Teleport>
<div id="container"></div>

遅延ハイドレーション(SSR)

defineAsyncComponent() が hydrate 戦略をサポートするようになりました:

import { defineAsyncComponent, hydrateOnVisible } from "vue";
const AsyncComp = defineAsyncComponent({
  loader: () => import("./Comp.vue"),
  hydrate: hydrateOnVisible(),
});

アンチパターン

アンチパターン問題点修正方法
defineProps() の分割代入 (Vue < 3.5)スナップショットを取得し、リアクティビティを失うprops.xxx でアクセスするか、toRefs() を使用する
分割代入されたPropsの watch() (Vue 3.5+)コンパイル時エラー — 分割代入されたPropsは直接監視できないgetterラッパーを使用する: watch(() => count, ...)
同じ要素での v-if + v-for実行順序が曖昧算出プロパティでフィルタリングした配列を使用する
v-for のキー = インデックス並び替え時に状態が壊れる安定したデータベースIDを使用する
Propsの変更単方向データフローに違反イベントを発行するか、v-model を使用する
ユーザーコンテンツでの v-htmlXSS脆弱性DOMPurifyでサニタイズする
Vue 3でのMixin不透明、衝突しやすいComposableに置き換える
Composable内のモジュールスコープの副作用インスタンス間で共有されるonMounted + onUnmounted でスコープを限定する
置き換え可能な状態への reactive()置き換えでリアクティビティが壊れる代わりに ref() を使用する
クリーンアップなしのウォッチャーメモリリーク、競合状態onCleanup または onWatcherCleanup() (Vue 3.5+) を使用する
新しいVue 3コードでのOptions APIエコシステムがComposition APIに移行中<script setup> を使用する
テンプレート参照のプレーンなref動的なrefサポートなし、名前一致が脆弱useTemplateRef() を使用する (Vue 3.5+)

関連スキル

  • accessibility — ARIA、セマンティックHTML、フォーカス管理
  • frontend-patterns — フレームワーク横断的なフロントエンドアーキテクチャ
  • typescript — Vueプロジェクトに適用されるTypeScriptのベストプラクティス
  • coding-standards — 一般的なコード品質基準

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

関連スキル