🔄

Vue.js初心者が学んだ11のこと(第2回)コンポーネント間通信とフォーム実装

に公開

はじめに

前回の記事では、Vue.js初心者が管理画面実装を通して学んだ基礎知識を紹介しました。

今回は、利用者管理機能の実装を通して学んだコンポーネント間通信フォーム実装のポイントを紹介します。

実装した機能:

  • 利用者登録・編集フォーム
  • モーダルUI
  • バリデーション
  • 親子コンポーネント通信

技術スタック:

  • Vue 3 (Composition API with <script setup>)
  • Pinia
  • Vite

1. Props/Emits - 親子コンポーネント通信の基本

親子コンポーネント間でデータをやり取りする基本パターンです。

データの流れ:

  • Props: 親 → 子へデータを渡す
  • Emits: 子 → 親へイベントを通知
<!-- 親コンポーネント (UsersView.vue) -->
<script setup>
import UserForm from '@/components/UserForm.vue'

const showModal = ref(false)

function handleUserSubmit(userData) {
  console.log('子から受け取ったデータ:', userData)
  // API送信処理など
}
</script>

<template>
  <UserForm
    :is-edit-mode="false"
    @submit="handleUserSubmit"
    @cancel="showModal = false"
  />
</template>
<!-- 子コンポーネント (UserForm.vue) -->
<script setup>
const props = defineProps({
  isEditMode: Boolean,
  user: Object
})

const emit = defineEmits(['submit', 'cancel'])

function handleSubmit() {
  emit('submit', formData)  // 親にデータを送信
}

function handleCancel() {
  emit('cancel')  // 親にキャンセルを通知
}
</script>

ポイント:

  • Props は読み取り専用(子で変更しない)
  • Emits でイベント名とデータを親に送る

2. v-model - 双方向データバインディング

入力フォームとデータを自動で同期させる便利な機能です。

<script setup>
import { reactive } from 'vue'

const formData = reactive({
  name: '',
  nickname: '',
  roomNumber: ''
})
</script>

<template>
  <input v-model="formData.name" type="text" />
  <input v-model="formData.nickname" type="text" />
  <input v-model="formData.roomNumber" type="text" />

  <!-- デバッグ用 -->
  <pre>{{ formData }}</pre>
</template>

動作:

  • input に入力 → formData.name が自動更新
  • formData.name を変更 → input の表示が自動更新

3. @submit.prevent - フォーム送信のデフォルト動作を防止

HTMLフォームのデフォルト動作(ページリロード)を防ぎ、JavaScriptで処理します。

<template>
  <!-- .prevent を付けないと、submitでページがリロードされる -->
  <form @submit.prevent="handleSubmit">
    <input v-model="formData.name" required />
    <button type="submit">登録</button>
  </form>
</template>

<script setup>
function handleSubmit() {
  // ページリロードされずにこの関数が実行される
  console.log('送信データ:', formData)
}
</script>

.prevent の意味:

  • event.preventDefault() の省略形
  • Vueの便利な修飾子

4. ref vs reactive - 2つのリアクティブ変数の使い分け

Vue 3では、リアクティブ変数を作る方法が2つあります。

ref() - プリミティブ値用

import { ref } from 'vue'

const count = ref(0)
const message = ref('Hello')

// JavaScript内では.valueでアクセス
count.value++
console.log(message.value)
<template>
  <!-- テンプレート内では.value不要 -->
  <p>{{ count }}</p>
  <p>{{ message }}</p>
</template>

reactive() - オブジェクト用

import { reactive } from 'vue'

const formData = reactive({
  name: '',
  email: '',
  age: 0
})

// .valueは不要(直接アクセス)
formData.name = '山田太郎'
formData.age++

使い分けのポイント:

  • 単一の値ref()
  • 複数のフィールドを持つオブジェクトreactive()

5. computed - 自動計算されるリアクティブ値

依存する値が変わると自動で再計算される値を作れます。

<script setup>
import { reactive, computed } from 'vue'

const formData = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// formDataが変わると自動再計算
const isFormValid = computed(() => {
  return formData.name.length > 0 &&
         formData.email.includes('@') &&
         formData.password.length >= 8 &&
         formData.password === formData.confirmPassword
})
</script>

<template>
  <button type="submit" :disabled="!isFormValid">
    登録
  </button>
</template>

メリット:

  • 手動で更新する必要がない
  • キャッシュされる(依存値が変わらない限り再計算しない)

6. :class - 動的クラスバインディング

条件に応じてCSSクラスを動的に切り替えられます。

パターン1: オブジェクト形式

<script setup>
const hasError = ref(false)
</script>

<template>
  <!-- hasErrorがtrueの時だけ 'error' クラスが付く -->
  <input
    v-model="formData.email"
    :class="{ error: hasError }"
  />
</template>

<style>
.error {
  border-color: red;
  background-color: #fee;
}
</style>

パターン2: 配列形式

<template>
  <!-- 常に 'input' クラス、hasErrorがtrueの時は 'error' も付く -->
  <input :class="['input', { error: hasError }]" />
</template>

7. :disabled - フィールドの読み取り専用化

編集不可にしたいフィールドを制御します。

<script setup>
const props = defineProps({
  isEditMode: Boolean,  // 編集モードかどうか
  user: Object
})
</script>

<template>
  <!-- 新規登録時は編集可、編集時は読み取り専用 -->
  <label>ユーザーID</label>
  <input
    v-if="isEditMode"
    :value="props.user.id"
    type="text"
    disabled
  />

  <label>氏名</label>
  <input
    v-model="formData.name"
    :disabled="isEditMode"
    required
  />

  <label>デバイスID</label>
  <input
    v-model="formData.deviceId"
    :disabled="isEditMode"
    required
  />
</template>

<style>
input:disabled {
  background-color: #f5f5f5;
  cursor: not-allowed;
}
</style>

使い分け:

  • v-if="isEditMode": 編集時のみ表示
  • :disabled="isEditMode": 編集時は読み取り専用

8. @click.stop - イベントバブリング防止

イベントが親要素に伝播するのを防ぎます。

<template>
  <!-- モーダル背景 -->
  <div class="modal-overlay" @click="closeModal">
    <!-- モーダル本体 -->
    <!-- .stopを付けないと、モーダル内をクリックしても閉じてしまう -->
    <div class="modal-content" @click.stop>
      <h2>利用者登録</h2>
      <UserForm @submit="handleSubmit" />
    </div>
  </div>
</template>

動作:

  • モーダル背景クリック → closeModal 実行(モーダルが閉じる)
  • モーダル本体クリック → イベント伝播が停止(モーダルは閉じない)

9. v-if - 条件付きレンダリング

条件に応じて要素の表示/非表示を切り替えます。

<script setup>
const showModal = ref(false)
const editingUser = ref(null)
</script>

<template>
  <!-- showModalがtrueの時だけDOMに追加される -->
  <div v-if="showModal" class="modal-overlay">
    <div class="modal-content">
      <h2>{{ editingUser ? '編集' : '新規登録' }}</h2>
      <UserForm
        :is-edit-mode="!!editingUser"
        :user="editingUser"
      />
    </div>
  </div>
</template>

v-if vs v-show:

  • v-if: DOMから完全に削除/追加(重い処理)
  • v-show: display: none で隠すだけ(軽い処理)

頻繁に切り替わるなら v-show、そうでないなら v-if を使う。

10. Template Literals - 文字列の変数埋め込み

バッククォート (`) を使って、文字列内に変数を埋め込めます。

// 古い書き方
const url = API_BASE_URL + '/api/users/' + userId

// 新しい書き方(ES6+)
const url = `${API_BASE_URL}/api/users/${userId}`

// 複雑な例
const message = `${user.nickname}さん、朝のお薬の時間です。`

メリット:

  • 読みやすい
  • + による文字列連結より直感的

11. Object Shorthand - オブジェクトプロパティの省略記法

プロパティ名と変数名が同じ場合、省略できます。

// 通常の書き方
const config = {
  signal: signal,
  timeout: timeout
}

// 省略形
const config = {
  signal,
  timeout
}

// 実際の使用例
const response = await apiClient.get(`${API_BASE_URL}/api/users`, { signal })
// ↑ これは { signal: signal } と同じ

まとめ

Vue.js初心者が利用者管理画面実装を通して学んだ11のポイントを紹介しました。

特に重要だったポイント:

  1. Props/Emits - 親子コンポーネント通信の基本
  2. v-model - フォーム実装の必須機能
  3. ref vs reactive - 適切な使い分け
  4. computed - 自動計算による効率化

次回は、状態管理とリアルタイム更新について紹介します。

参考リンク

Discussion