0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue.jsにおけるコンポーネント間のデータやり取りまとめ

Posted at

Vue3におけるデータのやり取り

Vue.js では、親子間、兄弟間、深い階層間など、さまざまな場面で コンポーネント間のデータやイベントのやり取り が求められます。

この章では、まず Vue 3 の代表的なデータのやり取り手段をすべて網羅的に紹介し、それぞれの特徴・方向・用途を比較します。


✅ データのやり取り方法一覧表

方法 データの流れ 主な用途例 特徴
defineProps 親 → 子 表示内容の渡し、初期値 readonly、型定義可能
defineEmits 子 → 親 ボタン操作、バリデーション結果の通知など イベント送信、型定義で安全に
defineModel 親 ⇄ 子(双方向) v-model でフォーム同期 props + emits を簡略化、Vue 3.4+
slot 親 → 子 親からのテンプレート挿入 レイアウトや柔軟な表示、再利用性向上
スコープ付き slot 親 ← 子 子の値を親に渡して表示変更 v-slot="{ data }" 形式でデータ取得
provide / inject 上位 → 下位 コンテキストの共有、テーマ、ロケールなど props のバケツリレー不要、非親子間もOK
$attrs 親 → 子(属性継承) コンポーネントのラップ(例:BaseButton) 未定義の props や DOM 属性が自動で渡る
ref(テンプレート) 親 → 子 子のメソッド呼び出しや値取得 $refs は非推奨、Composition API の ref() 使用
グローバル状態管理(Pinia) 任意間 ⇄ 任意間 複数コンポーネント間の状態共有、API結果保持 Vue 公式の状態管理ライブラリ、Vuexの後継
URL パラメータ / クエリ ルート ↔ ページ間 画面遷移時の ID や状態受け渡し useRoute() で取得、router.push() で変更可能
カスタムディレクティブ 親 → 子 DOM に特定の動作を加える(例:自動フォーカス) v-directive 形式、DOM操作を伴う
イベントバス(非推奨) 任意間 グローバルイベント通知(旧手法) Vue 3 では非推奨。Pinia または emits で代替

🧭 使用シーン別のおすすめ手段まとめ

シーン 推奨される手段
親 → 子へのデータ渡し definePropsslot
子 → 親への通知 defineEmitsdefineModel
双方向でデータを同期させたい場合(フォームなど) defineModel(Vue 3.4+)
深い階層に設定値や状態を渡したい provide / inject
離れたコンポーネント間で状態を共有したい Pinia
表示を柔軟にカスタマイズしたい slot、スコープ付きスロット
画面遷移時に値を受け渡したい URLパラメータ / クエリ
子の状態やメソッドを親から操作したい テンプレート上の ref
カスタムUIの属性を透過したい $attrs

💡 実務でよくある判断ポイント

判断基準 選ぶべき手段例
明確な親子関係がある defineProps, defineEmits
離れた位置や兄弟間で共有が必要 Pinia, provide/inject
コンポーネント再利用性を高めたい slot, $attrs
コンテキスト(言語・テーマ)を渡したい provide/inject
グローバルな状態・API結果を保持したい Pinia
双方向のフォーム制御が必要 defineModel or v-model + emits

📎 備考

  • Vue 3.4 以降は defineModel の導入により、v-model の記述がより直感的に
  • スロットや inject は便利だが、依存関係の可視性が落ちるため、適切な設計が重要
  • 状態管理が複雑化しやすい場面では Pinia に移行する判断が実務的

📗 defineProps / defineEmits / defineModel の実践活用【基本設計とベストプラクティス編】

Vue 3 のコンポーネント開発において最も頻出する3つのデータ連携手段:

  • defineProps
  • defineEmits
  • defineModel

これらの特徴・使い方・設計の勘所を詳しく見ていきましょう。


1️⃣ defineProps:親から子へ安全に値を渡す

// 子コンポーネント
<script setup lang="ts">
const props = defineProps<{ title: string }>()
</script>

<template>
  <h1>{{ props.title }}</h1>
</template>

設計ポイント:

  • 型で安全に明示する
  • 初期値やデフォルトは基本的に親で持つ

アンチパターン:

  • props を直接変更
  • 不要な ref() 包み

2️⃣ defineEmits:子から親へのイベント通知

// 子コンポーネント
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'submit', payload: { name: string }): void
}>()

function handleClick() {
  emit('submit', { name: 'Taro' })
}
</script>

設計ポイント:

  • イベント名は意味のある動詞に
  • payload はオブジェクト型にして柔軟に

アンチパターン:

  • 型定義せず emit を使う
  • 子が emit のタイミングを複雑化

3️⃣ defineModel:双方向バインディングを簡潔に

// 子コンポーネント
<script setup lang="ts">
const model = defineModel<string>()
</script>

<template>
  <input v-model="model" />
</template>
// 複数 v-model の例
const title = defineModel<string>('title')
const content = defineModel<string>('content')

設計ポイント:

  • Vue 3.4+ で使用可
  • v-model の命名に一貫性を持たせる

アンチパターン:

  • defineModeldefineProps を同時に使いデータが二重管理される
  • v-model 名が不明瞭(例:v-model="data"

🔄 選び方早見表

目的 推奨手段 補足
一方向データ渡し defineProps readonly。明確な受け渡し
子 → 親の通知 defineEmits props との責務分離が重要
フォーム入力などの同期 defineModel v-model を使うことで直感的になる

🧾 まとめ:Vue 3におけるデータやり取りの全体像と設計の勘所

Vue 3 の Composition API により、データとイベントの設計がより明示的かつ型安全になりました。

目的に応じた正しい手段を選ぶことで、再利用性と保守性の高い設計が実現します。


✅ 総まとめ

状況 最適な手段
明確な親子関係 defineProps, defineEmits
双方向バインディングが必要 defineModel
非親子間のデータ共有 provide/inject, Pinia
表示の柔軟性・拡張性が欲しい slot, $attrs
状態や依存関係が複雑になりそうな場合は Pinia に集約 Pinia を検討

🎯 実務上のアドバイス

  • 責務を明確に: props(入力) / emit(通知) / model(同期)
  • バケツリレーを避けたい時は inject/Pinia の検討
  • フォーム・再利用部品には defineModel が効果的
  • v-model と emits の併用は責務の混乱を招くため注意
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?