Props - 親子コンポーネント間の入力契約
ロードマップ: Vue.js学習ロードマップ
What(何についてか)
props は、親コンポーネントから子コンポーネントへ値を渡すための仕組みであり、子コンポーネントにとっての外部入力を表す。Vue では、子コンポーネントがどの props を受け取るかを明示的に宣言する。
Why(なぜ必要か)
props を明示宣言することで、コンポーネントの外部インターフェースをコード上で明確にできる。これは関数の引数宣言に近い役割を持つ。
- 親子間のデータ受け渡しを明示できる
- どの値が外部から入るかを可視化できる
- 型や default, validator を通じて入力契約を定義できる
- 一方向データフローにより状態の所有者を明確に保てる
How(どう動くか)
Props Declaration
<script setup> では defineProps() を用いて props を宣言する。
<script setup lang="ts">
defineProps<{
title?: string
likes?: number
}>()
</script>配列形式やオブジェクト形式でも宣言できるが、TypeScript 利用時は型ベース宣言が自然である。
<script setup>
defineProps({
title: String,
likes: Number
})
</script>Reactive Props Destructure
Vue 3.5+ では、defineProps() の結果を分割代入しても、同一の <script setup> ブロック内で参照される限りコンパイラが props.foo へ補完する。
<script setup>
const { foo } = defineProps(['foo'])
watchEffect(() => {
console.log(foo)
})
</script>ただし、watch(foo, ...) のように値をそのまま監視対象へ渡すことはできない。リアクティブな参照として扱うには getter で包む必要がある。
watch(() => foo, () => {
// ...
})保守性の観点での運用判断
分割代入は便利だが、watch, composable, computed などリアクティビティの境界で挙動差が見えやすい。保守性を優先する場合は、常に props.foo を明示する運用も合理的である。
const props = defineProps<{ foo: string }>()
watch(() => props.foo, () => {
// ...
})Prop Passing Details
子コンポーネント側では camelCase で props を宣言し、親テンプレートでは kebab-case で渡すのが標準的。
<script setup>
defineProps({
greetingMessage: String
})
</script><MyComponent greeting-message="hello" />値の受け渡しでは、文字列リテラル以外は v-bind(:)を使うのが基本になる。
<MyComponent title="hello" />
<MyComponent :likes="42" />
<MyComponent :disabled="false" />
<MyComponent :items="items" />固定文字列を : 付きで渡す場合は JavaScript 式として文字列化する必要がある。
<MyComponent :title="'hello'" />One-Way Data Flow
props は親から子への一方向データフローを形成する。子コンポーネントで props を直接変更してはいけない。
const props = defineProps(['foo'])
// props.foo = 'bar' // NG子側で値を加工したい場合は、用途に応じてローカル state か computed を使う。
const props = defineProps<{ initialCounter: number; size: string }>()
const counter = ref(props.initialCounter)
const normalizedSize = computed(() => props.size.trim().toLowerCase())実務上の判断軸
flowchart TD A["子で props を使いたい"] --> B{"用途は何か"} B -->|"そのまま参照"| C["props.foo を読む"] B -->|"初期値として使う"| D["ref(props.foo) でローカル化"] B -->|"加工して使う"| E["computed で派生値を作る"] B -->|"親へ変更を返したい"| F["emit または v-model を検討"]
Prop Validation
props には型、必須性、デフォルト値、独自バリデーションを付与できる。
<script setup>
defineProps({
title: {
type: String,
required: true
},
likes: {
type: Number,
default: 0
},
status: {
type: String,
validator: (value) => ['success', 'warning', 'danger'].includes(value)
}
})
</script>配列やオブジェクトの default は factory function で返す。
items: {
type: Array,
default: () => []
}Zod との役割分担
Zod は props 宣言そのものの置き換えではなく、外部データや複雑なオブジェクト構造の厳密検証に向く。props は Vue のコンポーネント機構に統合された入力契約であり、required, default, Boolean casting などの役割を持つ。
- props は Vue の公式インターフェース定義
- Zod は値の中身を精密検証する補強材
- 単純 props は Vue 標準で十分
- 外部入力やネストの深い構造には Zod 併用が有効
Options API → Composition API 差分
| 項目 | Options API(旧) | Composition API(新) |
|---|---|---|
| props宣言 | props: { ... } を options に記述 | defineProps() で宣言する |
| props参照 | this.foo | props.foo または destructure |
| 型付け | TypeScript統合はやや間接的 | <script setup lang="ts"> で自然に記述できる |
| 入力契約の見え方 | options 内にまとまる | setup 冒頭で明示できる |
Key Concepts
| 用語 | 説明 |
|---|---|
| Props | 親から子へ渡される、子コンポーネントの外部入力 |
defineProps() | <script setup> で props を宣言するためのマクロ |
| One-Way Data Flow | 親から子へだけ値が流れる設計原則 |
| Prop Validation | 型、required、default、validator による入力契約定義 |
| Reactive Props Destructure | defineProps() の分割代入を Vue 3.5+ でリアクティブに扱う仕組み |
v-bind / : | 値を JavaScript 式として props に渡す記法 |
| Zod | Vue props の代替ではなく、外部データ検証を補強するスキーマライブラリ |