ycs77-vue について
ycs77-vue は、読みやすくボイラープレートの少ないコードを目指す Vue 3 と TypeScript の開発者向けコーディング規約スキルです。コンポーネント構成、型宣言、リアクティブな状態管理の規則をまとめています。
主な機能
- 01TypeScript の書式規約
- 02Vue SFC のブロック順序
- 03props と emits のインライン型
- 04reactive() によるフォーム状態管理
- 05複雑な型に対する ref() の書き方
- 06テンプレート内の props アクセス規則
活用シーン
- 01Vue 3 プロジェクトのコードスタイル統一
- 02コンポーネントの props と emits の型整理
- 03フォームや複雑なリアクティブ状態の管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ycs77/ skills --skill ycs77-vueインストール後、Claude Code で/ycs77-vueを入力して読み込まれたか確認します。
詳細
Lucas Yang の Vue 規約
実際のプロジェクトで、ボイラープレートを最小限に抑え、読みやすさと実用的なシンプルさを重視する、独自の見解に基づいた Vue 3 と TypeScript のパターンです。
TypeScript のフォーマット
標準:スペース 2 つ、シングルクォート、セミコロンなし、末尾のカンマあり。
Vue SFC のパターン
1. SFC のブロック順序
Single File Components では、常に <template> を <script setup lang="ts"> より前に配置します。これは、構造(何をレンダリングするか)から動作(どのように機能するか)へと進む、自然な読み順に沿ったものです。
推奨:
<template>
<div class="container">
<h1>{{ title }}</h1>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const title = ref('Hello World')
</script>
2. props と emits のインライン型
コンポーネント API が呼び出し元から見えるように、defineProps と defineEmits ではインライン型を使用します。
推奨:
<script setup lang="ts">
const props = defineProps<{
title: string
count?: number
items: {
id: string
name: string
}[]
}>()
const emit = defineEmits<{
update: [value: string]
delete: [id: string]
}>()
</script>
例外:型が複数のコンポーネントで再利用される場合、またはエクスポートが必要な場合に限り、別のインターフェースに切り出します。
3. フォームの状態には reactive() を使用する
フォームの状態を管理する場合は、すべてのフォームフィールドを保持する 1 つのリアクティブオブジェクトを作成するために、reactive() を使用します。この方法では、各フィールドに複数の ref() を使う場合と比べて、状態管理が簡潔になり、ボイラープレートコードを減らせます。
推奨:
<template>
<form @submit.prevent="handleSubmit">
<input v-model="form.email" type="email">
<input v-model="form.password" type="password">
<input v-model="form.rememberMe" type="checkbox">
</form>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const form = reactive({
email: '',
password: '',
rememberMe: false,
})
function handleSubmit() {
console.log(form) // Clean object or reactive object, easy to submit
}
function resetForm() {
form.email = ''
form.password = ''
form.rememberMe = false
}
</script>
理由:関連する状態をまとめることで、.value のボイラープレートが減り、フォームの送信処理もすっきりします。個々の ref() は、本当に独立した状態にのみ使用します。
4. 複雑な型の ref() には型アサーションを使用する
インターフェース型や複雑なオブジェクト型には、Vue のディープアンラップによる型の競合を避けるため、ref<Type>(value) よりも ref(value) as Ref<Type> を使用します。値がアサーションした型を満たしていることを確認してください。プリミティブ型と列挙型には ref<Type>() を使用します。
オブジェクトが存在しない場合は、undefined よりも null を使用します。
推奨:
<script setup lang="ts">
import type { Ref } from 'vue'
import { ref } from 'vue'
interface User {
id: number
name: string
}
enum Status {
Pending = 'pending',
}
// Complex type - use type assertion
const user = ref({ id: 1, name: 'Lucas' }) as Ref<User>
const users = ref([]) as Ref<User[]>
const isSelectedUser = ref(null) as Ref<User | null>
// Primitive types - generic parameter is fine
const count = ref<number>(0)
const isActive = ref<boolean>(false)
const status = ref<Status>(Status.Pending)
</script>
避ける:
<script setup lang="ts">
import type { User } from '@/types'
import { ref } from 'vue'
// May cause: Type '...' is not assignable to type 'Ref<User>'
const user = ref<User>()
</script>
5. バインディングには同名の省略記法を使用する
Vue 3.4 以降では、一致するバインディングに同名の省略記法を使用します。
推奨:
<template>
<div :id :title>
<MyComponent :user-name :count :is-active />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const id = ref('app')
const title = ref('Dashboard')
const userName = ref('Lucas')
const count = ref(42)
const isActive = ref(true)
</script>
理由::user-name は userName に対応します(ケバブケースからキャメルケースへの変換)。セクション 6 のように、props. による明示的なアクセスが必要な場合は、省略せずに記述します。
6. テンプレートから props に直接アクセスする
defineProps で宣言した props は、<template> 内で名前を使って参照できます。曖昧さがない場合は、{{ props.title }} よりも {{ title }} を使用します。ローカルバインディングが prop と名前の競合を起こす場合は、props.name を使用します。class(JavaScript の予約語)や as(TypeScript の型アサーションで使用)のように、構文上注意が必要な名前には、props.class や props.as のように明示的にアクセスします。:class の省略記法では、式として単独の class を使用できません。また、:is="as" では、as の出どころが明確になりません。
推奨:
<template>
<div :class="props.class">
<h1>{{ title }}</h1>
<component :is="props.as">content</component>
</div>
</template>
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
const props = defineProps<{
title: string
as: string
class?: HTMLAttributes['class']
}>()
</script>
避ける:
<div :class>
<component :is="as">content</component>
</div>