Développement Vue avec Claude Code
Découvrez développement Vue avec Claude Code. Conseils pratiques et exemples de code inclus.
Vue.js開発でClaude Codeが活躍する場面
Vue.jsはComposition APIやPiniaストアなど、モダンな設計パターンが豊富です。Claude Codeはこれらのパターンを理解した上で、型安全で保守性の高いVueコンポーネントを効率的に生成できます。
Composition APIコンポーネントの生成
> Vue 3のComposition APIでデータテーブルコンポーネントを作成して。
> ソート、フィルター、ページネーション機能を実装して。
> TypeScriptで型安全にして。
<script setup lang="ts">
import { ref, computed } from 'vue';
interface Column<T> {
key: keyof T;
label: string;
sortable?: boolean;
}
interface Props<T> {
data: T[];
columns: Column<T>[];
pageSize?: number;
}
const props = withDefaults(defineProps<Props<Record<string, unknown>>>(), {
pageSize: 10,
});
const sortKey = ref<string>('');
const sortOrder = ref<'asc' | 'desc'>('asc');
const filterText = ref('');
const currentPage = ref(1);
const filteredData = computed(() => {
if (!filterText.value) return props.data;
const query = filterText.value.toLowerCase();
return props.data.filter(row =>
Object.values(row).some(val =>
String(val).toLowerCase().includes(query)
)
);
});
const sortedData = computed(() => {
if (!sortKey.value) return filteredData.value;
return [...filteredData.value].sort((a, b) => {
const aVal = a[sortKey.value];
const bVal = b[sortKey.value];
const cmp = String(aVal).localeCompare(String(bVal));
return sortOrder.value === 'asc' ? cmp : -cmp;
});
});
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * props.pageSize;
return sortedData.value.slice(start, start + props.pageSize);
});
const totalPages = computed(() =>
Math.ceil(filteredData.value.length / props.pageSize)
);
function handleSort(key: string) {
if (sortKey.value === key) {
sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc';
} else {
sortKey.value = key;
sortOrder.value = 'asc';
}
}
</script>
<template>
<div>
<input
v-model="filterText"
type="text"
placeholder="検索..."
class="mb-4 w-full rounded border px-3 py-2"
@input="currentPage = 1"
/>
<table class="w-full border-collapse">
<thead>
<tr>
<th
v-for="col in columns"
:key="String(col.key)"
class="cursor-pointer border-b px-4 py-2 text-left"
@click="col.sortable && handleSort(String(col.key))"
>
{{ col.label }}
<span v-if="sortKey === col.key">
{{ sortOrder === 'asc' ? ' ↑' : ' ↓' }}
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in paginatedData" :key="i" class="hover:bg-gray-50">
<td v-for="col in columns" :key="String(col.key)" class="border-b px-4 py-2">
{{ row[col.key] }}
</td>
</tr>
</tbody>
</table>
<div class="mt-4 flex items-center justify-between">
<span class="text-sm text-gray-600">
{{ filteredData.length }}件中 {{ (currentPage - 1) * pageSize + 1 }}〜{{ Math.min(currentPage * pageSize, filteredData.length) }}件
</span>
<div class="flex gap-2">
<button
:disabled="currentPage <= 1"
class="rounded border px-3 py-1 disabled:opacity-50"
@click="currentPage--"
>
Previous
</button>
<button
:disabled="currentPage >= totalPages"
class="rounded border px-3 py-1 disabled:opacity-50"
@click="currentPage++"
>
Next
</button>
</div>
</div>
</div>
</template>
Piniaストアの生成
> Piniaでショッピングカートストアを作成して。
> 商品追加、数量変更、合計金額計算を実装して。
// stores/cart.ts
import { defineStore } from 'pinia';
interface CartItem {
productId: string;
name: string;
price: number;
quantity: number;
image: string;
}
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[],
}),
getters: {
totalItems: (state) =>
state.items.reduce((sum, item) => sum + item.quantity, 0),
totalPrice: (state) =>
state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
formattedTotal(): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(this.totalPrice);
},
},
actions: {
addItem(product: Omit<CartItem, 'quantity'>) {
const existing = this.items.find(i => i.productId === product.productId);
if (existing) {
existing.quantity++;
} else {
this.items.push({ ...product, quantity: 1 });
}
},
updateQuantity(productId: string, quantity: number) {
const item = this.items.find(i => i.productId === productId);
if (!item) return;
if (quantity <= 0) {
this.removeItem(productId);
} else {
item.quantity = quantity;
}
},
removeItem(productId: string) {
this.items = this.items.filter(i => i.productId !== productId);
},
clearCart() {
this.items = [];
},
},
persist: true,
});
コンポーザブルの作成
> APIフェッチ用のコンポーザブルを作成して。
> ローディング、エラー、リトライ機能付きで。
// composables/useFetch.ts
import { ref, watchEffect, type Ref } from 'vue';
interface UseFetchOptions<T> {
immediate?: boolean;
defaultValue?: T;
transform?: (data: unknown) => T;
}
export function useFetch<T>(
url: string | Ref<string>,
options: UseFetchOptions<T> = {}
) {
const data = ref<T | null>(options.defaultValue ?? null) as Ref<T | null>;
const error = ref<Error | null>(null);
const loading = ref(false);
async function execute() {
loading.value = true;
error.value = null;
try {
const resolvedUrl = typeof url === 'string' ? url : url.value;
const res = await fetch(resolvedUrl);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
data.value = options.transform ? options.transform(json) : json;
} catch (err) {
error.value = err as Error;
} finally {
loading.value = false;
}
}
async function retry() {
await execute();
}
if (options.immediate !== false) {
if (typeof url !== 'string') {
watchEffect(() => execute());
} else {
execute();
}
}
return { data, error, loading, execute, retry };
}
Nuxt.jsでのサーバーAPIルート
// server/api/products/index.get.ts
export default defineEventHandler(async (event) => {
const query = getQuery(event);
const page = Number(query.page) || 1;
const limit = Number(query.limit) || 20;
const products = await prisma.product.findMany({
skip: (page - 1) * limit,
take: limit,
orderBy: { createdAt: 'desc' },
});
const total = await prisma.product.count();
return {
items: products,
total,
page,
totalPages: Math.ceil(total / limit),
};
});
テスト
import { describe, it, expect } from 'vitest';
import { setActivePinia, createPinia } from 'pinia';
import { useCartStore } from '@/stores/cart';
describe('Cart Store', () => {
beforeEach(() => {
setActivePinia(createPinia());
});
it('should add item to cart', () => {
const cart = useCartStore();
cart.addItem({ productId: '1', name: 'Test', price: 1000, image: '/test.jpg' });
expect(cart.items).toHaveLength(1);
expect(cart.totalItems).toBe(1);
});
it('should increment quantity for existing item', () => {
const cart = useCartStore();
cart.addItem({ productId: '1', name: 'Test', price: 1000, image: '/test.jpg' });
cart.addItem({ productId: '1', name: 'Test', price: 1000, image: '/test.jpg' });
expect(cart.items).toHaveLength(1);
expect(cart.items[0].quantity).toBe(2);
});
it('should calculate total price', () => {
const cart = useCartStore();
cart.addItem({ productId: '1', name: 'A', price: 1000, image: '' });
cart.addItem({ productId: '2', name: 'B', price: 2000, image: '' });
expect(cart.totalPrice).toBe(3000);
});
});
Summary
Claude Codeを使えば、Vue.js / Nuxt.jsのコンポーネント、ストア、コンポーザブル、APIルートを型安全かつ効率的に開発できます。Vue特有のComposition APIパターンやPiniaの設計もClaude Codeは熟知しています。プロンプトの書き方次第で品質が変わるため、プロンプトテクニック完全ガイドを参考にしてください。プロジェクトのVue規約はCLAUDE.mdに記述しておくとよいでしょう。
Claude Codeの詳細はAnthropic公式ドキュメントをご覧ください。Vue.jsの公式ガイドはVue.js公式サイトを参照してください。
PDF gratuit : aide-mémoire Claude Code en 5 minutes
Laissez simplement votre e-mail et nous vous enverrons immédiatement l'aide-mémoire A4 en PDF.
Nous traitons vos données avec soin et n'envoyons jamais de spam.
À propos de l'auteur
Masa
Ingénieur passionné par Claude Code. Il gère claudecode-lab.com, un média tech en 10 langues avec plus de 2 000 pages.
Articles similaires
7 vérifications avant de publier chaque jour un article multilingue sur Claude Code
Une checklist pratique pour publier des articles multilingues sur Claude Code chaque jour sans oublier une langue, casser les CTA ou laisser l’ancien contenu en production.
Codex Automations : confier l'analyse, les articles et le deploiement a l'IA
Guide pratique pour utiliser Codex Automations dans une operation de contenu orientee monetisation.
Claude Code × GCP Cloud Functions Guide Complet | Développement Serverless Ultra-Rapide
Optimisez GCP Cloud Functions avec Claude Code. Implémentez des triggers HTTP/Pub/Sub/Firestore, des tests locaux et l'automatisation des déploiements avec des exemples de code réels de l'expérience de Masa.