Desarrollo con Vue y Claude Code
Aprenda sobre desarrollo con Vue usando Claude Code. Incluye ejemplos practicos de codigo.
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 gratuito: Hoja de trucos de Claude Code en 5 minutos
Solo deja tu correo y te enviaremos al instante la hoja de trucos en una página A4.
Cuidamos tus datos personales y nunca enviamos spam.
Sobre el autor
Masa
Ingeniero apasionado por Claude Code. Dirige claudecode-lab.com, un medio tecnológico en 10 idiomas con más de 2.000 páginas.
Artículos relacionados
7 comprobaciones antes de publicar cada día un artículo multilingüe sobre Claude Code
Una lista práctica para publicar artículos multilingües sobre Claude Code todos los días sin olvidar idiomas, romper CTAs ni dejar páginas antiguas en producción.
Que es Codex Automations y como dejar que la IA gestione contenido mientras duermes
Guia practica para usar Codex Automations en analitica, articulos, CTA, despliegue y monetizacion.
Claude Code × GCP Cloud Functions Guía Completa | Desarrollo Serverless Ultrarrápido
Optimiza GCP Cloud Functions con Claude Code. Implementa triggers HTTP/Pub/Sub/Firestore, pruebas locales y automatización de despliegues con ejemplos de código reales de la experiencia de Masa.