vue-composition について
Vue アプリケーションを開発する方向けの、Vue 3 Composition API リファレンススキルです。リアクティビティやコンポーザブルに加え、セキュリティ、エラー処理、パフォーマンス、アクセシビリティ、テストを扱います。
主な機能
- 01リアクティビティ API と状態
- 02コンポーザブル関数のパターン
- 03コンポーネントと props の実践
- 04セキュリティとエラー処理
- 05パフォーマンスとアクセシビリティ
- 06テスト設定
活用シーン
- 01再利用可能なコンポーザブルの作成
- 02コンポーネントのリアクティブな状態管理
- 03Vue アプリのセキュリティと性能の確認
MCP 対応
包括的な Vue ドキュメントの参照先として `mcp__documentation__fetch_docs` を案内します。
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill vue-compositionインストール後、Claude Code で/vue-compositionを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
advanced.mdquick-ref/composables.mdreactivity-cheatsheet.md詳細
Vue 3 Composition API
完全なリファレンス:WebSocket コンポーザブル、provide/inject プラグインパターン、Socket.IO の統合、ルーム管理については、advanced.md を参照してください。
詳細な知識:包括的なドキュメントについては、technology:
vueを指定してmcp__documentation__fetch_docsを使用してください。
このスキルを使用しない場合
次の場合は、このスキルを使用しないでください。
- Vue 2 Options API を使用する場合(legacy Vue docs を使用してください)
- React アプリケーションを構築する場合(
frontend-reactを使用してください) - Angular フレームワークを使用する場合(
angularを使用してください) - Svelte を使用する場合(
svelteを使用してください) - サーバーサイドのみのロジックを扱う場合(フレームワークは不要です)
コンポーネントの構成
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
interface Props {
title: string
count?: number
}
const props = defineProps<Props>()
const emit = defineEmits<{
update: [value: string]
}>()
const localState = ref('')
const doubled = computed(() => props.count * 2)
onMounted(() => {
console.log('Component mounted')
})
</script>
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ doubled }}</p>
</div>
</template>
リアクティビティシステム
| API | 目的 |
|---|---|
ref() | プリミティブなリアクティブ値 |
reactive() | リアクティブオブジェクト |
computed() | 派生状態 |
watch() | リアクティブソースを監視 |
watchEffect() | 依存関係を自動追跡 |
コンポーザブルのパターン
// useCounter.ts
export function useCounter(initial = 0) {
const count = ref(initial)
const increment = () => count.value++
const decrement = () => count.value--
return { count, increment, decrement }
}
主な概念
<script setup>が推奨される構文です- プリミティブには
ref、オブジェクトにはreactiveを使用します - 双方向バインディングには
v-modelを使用します - コンテンツの配信にはスロットを使用します
アンチパターン
| アンチパターン | 問題点 | 正しい方法 |
|---|---|---|
プリミティブに reactive() を使用する | 分割代入するとリアクティビティが失われます | プリミティブには ref() を使用します |
| props を直接変更する | 一方向のデータフローが崩れます | イベントを発行し、v-model を使用します |
サニタイズせずに v-html を使用する | XSS の脆弱性につながります | レンダリング前に DOMPurify を使用します |
| メモ化せずに大きな computed を使用する | レンダリングのたびに再計算されます | 小さな computed に分割します |
onUnmounted でクリーンアップしない | メモリリークが発生します | タイマーをクリアし、購読を解除します |
computed で十分な場合に watch を使用する | 不必要に複雑になります | 派生状態には computed を使用します |
よくある問題のトラブルシューティング
| 問題 | 考えられる原因 | 解決策 |
|---|---|---|
| Computed が更新されない | ref に .value を付け忘れている | .value で ref にアクセスします |
| テンプレートがリアクティブにならない | ref の代わりに let を使用している | ref() または reactive() に変換します |
| props の変更に関する警告 | props を直接変更している | props を複製するか、更新イベントを発行します |
| コンポーネントが再レンダリングされない | プリミティブに reactive を使用している | プリミティブには ref() を使用します |
| メモリリーク | クリーンアップを忘れている | onUnmounted でクリーンアップを追加します |
v-model が動作しない | イベント名が間違っている | update:modelValue イベントを使用します |
本番環境への準備
セキュリティのベストプラクティス
<script setup lang="ts">
// NEVER use v-html with user input without sanitization
import DOMPurify from 'dompurify'
const props = defineProps<{ userContent: string }>()
const sanitizedContent = computed(() => DOMPurify.sanitize(props.userContent))
</script>
<template>
<div v-html="userContent" />
<div v-html="sanitizedContent" />
</template>
// Validate external URLs
const isValidUrl = (url: string): boolean => {
try {
const parsed = new URL(url)
return ['http:', 'https:'].includes(parsed.protocol)
} catch {
return false
}
}
// Never expose secrets in client code
// Use runtime config or server routes instead
const config = useRuntimeConfig()
// config.public.* is safe for client
// config.* (without public) stays server-side
エラー処理
<script setup lang="ts">
import { onErrorCaptured } from 'vue'
// Component-level error boundary
onErrorCaptured((error, instance, info) => {
// Log to error tracking service
logError(error, { component: instance?.$options.name, info })
// Return false to prevent error propagation
return false
})
</script>
// Global error handler (main.ts)
const app = createApp(App)
app.config.errorHandler = (error, instance, info) => {
console.error('Global error:', error)
// Send to error tracking (Sentry, etc.)
captureException(error, { extra: { info } })
}
app.config.warnHandler = (msg, instance, trace) => {
// Log warnings in development
if (import.meta.env.DEV) console.warn(msg, trace)
}
パフォーマンスの最適化
<script setup lang="ts">
import { defineAsyncComponent, shallowRef } from 'vue'
// Lazy load heavy components
const HeavyChart = defineAsyncComponent({
loader: () => import('./HeavyChart.vue'),
loadingComponent: LoadingSpinner,
delay: 200,
errorComponent: ErrorDisplay,
})
// Use shallowRef for large objects that don't need deep reactivity
const largeDataset = shallowRef<DataItem[]>([])
// Computed with getter/setter for derived state
const filteredItems = computed(() =>
items.value.filter(item => item.active)
)
</script>
<template>
<header v-once>
<h1>{{ appTitle }}</h1>
</header>
<div v-for="item in list" :key="item.id" v-memo="[item.id, item.updated]">
<ExpensiveComponent :data="item" />
</div>
<VirtualList :items="largeDataset" :item-height="50" />
</template>
アクセシビリティ(a11y)
<template>
<button @click="handleClick">Submit</button>
<div role="alert" aria-live="polite" v-if="error">
{{ error }}
</div>
<dialog ref="dialogRef" @vue:mounted="dialogRef?.focus()">
<h2 id="dialog-title">Confirm Action</h2>
<div aria-labelledby="dialog-title">...</div>
</dialog>
</template>
テストのセットアップ
// Component testing with Vue Test Utils
import { mount } from '@vue/test-utils'
import { describe, it, expect, vi } from 'vitest'
describe('UserForm', () => {
it('emits submit with form data', async () => {
const wrapper = mount(UserForm)
await wrapper.find('input[name="email"]').setValue('[email protected]')
await wrapper.find('form').trigger('submit')
expect(wrapper.emitted('submit')).toBeTruthy()
expect(wrapper.emitted('submit')[0]).toEqual([{ email: '[email protected]' }])
})
})
監視するメトリクス
| メトリクス | アラートのしきい値 |
|---|---|
| Largest Contentful Paint(LCP) | > 2.5s |
| First Input Delay(FID) | > 100ms |
| Cumulative Layout Shift(CLS) | > 0.1 |
| JavaScript バンドルサイズ | > 200KB(gzip 圧縮後) |
| コンポーネントのレンダリング時間 | > 16ms |
ビルドの最適化
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vue: ['vue', 'vue-router', 'pinia'],
ui: ['@headlessui/vue', '@vueuse/core'],
},
},
},
sourcemap: true,
},
})
チェックリスト
- グローバルエラーハンドラーを設定済み
- クライアントの状態に機密データが含まれていない
- v-html のコンテンツに DOMPurify を使用
- コード分割に非同期コンポーネントを使用
- 大きな非リアクティブデータに shallowRef を使用
- コストの高いリストのレンダリングに v-memo を使用
- 長いリストに仮想スクロールを使用
- セマンティック HTML と ARIA ラベルを使用
- Core Web Vitals を監視
- バンドルサイズを最適化
- エラー報告サービスを統合
リファレンスドキュメント
詳細な知識:包括的なドキュメントについては、technology:
vueを指定してmcp__documentation__fetch_docsを使用してください。
コメント
Markdown 対応