Communitygithub.com

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

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

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

このスキルは、Vue.js 3 開発向けの包括的なガイドであり、Composition API(`<script setup>`)を使用したコンポーネント設計、リアクティビティ、状態管理、ルーティング、テスト、SSR(サーバーサイドレンダリング)パターンをカバーします。Nuxt に固有のガイダンスも、通常の Vue と異なる点に重点を置いて含まれています。このスキルは、Vue.js(任意のバージョン)、Nuxt、Vite + Vue、または Pinia を使用するプロジェクトで、Vue コンポーネントのアーキテクチャ、コンポーザブル、リアクティビティ、状態管理について質問がある場合や、Vue シングルファイルコンポーネント(`.vue` ファイル)のレビュー、Vue Router、Pinia ストア、Vite/Vitest の設定、Vue 固有のパフォーマンス、セキュリティ、または SSR パターンについて議論する際に有効化されます。 このスキルは、Claude Code、Cursor、Codex などの AI エージェント向けに設計されており、プロジェクト構造、コンポーネントアーキテクチャ、コンポーザブル、状態管理(Pinia)、ルーティング(Vue Router)、テンプレートパターン、パフォーマンス最適化、テスト、Nuxt 固有のパターン、Vue 3.5+ の新API(リアクティブプロップの分割代入、`useTemplateRef()`、`onWatcherCleanup()`、`useId()`、`defer` Teleport、遅延ハイドレーション)など、広範なトピックをカバーします。さらに、アンチパターン(`v-if` + `v-for` の同時使用、プロップの直接変更、`v-html` によるXSS脆弱性など)とその解決策を列挙し、コード品質を高めます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/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 コンポーネントのアーキテクチャ、コンポーザブル、リアクティビティ、または状態管理について質問している場合。
  • Vue シングルファイルコンポーネント(.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.tsコンポーザブル
camelCase.tsユーティリティ、APIクライアント、型
kebab-case ディレクトリルートセグメント、機能フォルダ

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

シングルファイルコンポーネントの順序

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

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

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

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

  • コンテナコンポーネント: データフェッチ、状態、副作用を所有します。プレゼンテーショナルコンポーネントをレンダリングします。
  • プレゼンテーショナルコンポーネント: プロップを受け取り、イベントを発行します。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. コンポーザブル(再利用可能なロジック)

構造

// 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 でクリーンアップします。
  • モジュールスコープの副作用は避けてください。

vs Mixins

コンポーザブルはVue 2のmixinを完全に置き換えます。

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

4. 状態管理

何をいつ使うか

パターンユースケース
ref() / reactive()ローカルコンポーネント状態
Props + Emits親子間通信
Provide / Injectテーマ、設定、プラグインAPI
Piniaストアグローバル、共有、複雑な状態
サーバー状態コンポーザブルキャッシュ付きAPIデータ(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(() => count, (newVal) => { ... }); // ゲッターラッパーが必要

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());
  // ... シグナルでフェッチ
});

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は直接監視できないゲッターラッパーを使用: watch(() => count, ...)
同じ要素に v-if + v-forあいまいな実行順序算出フィルタリング配列を使用する
v-for のキーにインデックス並び替えで状態が崩れる安定したデータベースIDを使用する
propsの変更単方向データフローに違反するイベントを発行するか v-model を使用する
ユーザーコンテンツでの v-htmlXSS脆弱性DOMPurifyでサニタイズする
Vue 3でのMixin不透明で衝突しやすいコンポーザブルに置き換える
コンポーザブル内のモジュールスコープの副作用インスタンス間で共有される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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

関連スキル