Vueコンポーネント解説：クリスマスカードアプリの作成

## はじめに
この記事では、VueとTypeScriptを使用してクリスマスカード表示アプリを構築する手順を解説します。コンポーネント指向のアーキテクチャを活用し、アプリを構築する際の基本的な手法や考え方に焦点を当てます。
作成するアプリは、クリスマスカードのデザインを選択し、差出人や受取人の名前、メッセージをカスタマイズできるシンプルなアプリケーションです。

## コンポーネントの概要
### コンポーネントの重要性
Vueにおけるコンポーネントの役割と重要性はアプリケーションを小さな部品に分割し、再利用性や保守性を高めるために欠かせない要素です。各コンポーネントが特定の機能やUI要素を担当し、相互に連携してアプリケーション全体を構築します。

### Vueにおけるコンポーネントの基本的な考え方
Vueでは、コンポーネントを使ったアプリケーションの構築が強力な特徴です。コンポーネントはそれぞれが独立しており、データや機能をカプセル化することで再利用可能な部品として活用できます。この考え方は大規模なアプリケーション開発において、保守性や開発効率を向上させるために重要です。

## 実装
前回の記事同様の環境なため実行環境は割愛します。
[Vueのデータバインディング](https://qiita.com/taka-d/items/9336ded4069b927b8363)参照
```App.vue
<script setup lang="ts">
import SiteHeader from "./components/SiteHeader.vue"; 
import SiteFooter from "./components/SiteFooter.vue"; 
</script>

<template>
  <div class="app">
    <SiteHeader />
    <main class="main-content">
      <router-view />
    </main>
    <SiteFooter />
  </div>
</template>

<style>
/* 必要なスタイルを追加 */
/* ... */
</style>
```
役割
・アプリケーション全体のコンテナとなる親コンポーネントです。
・今回はSiteHeader コンポーネントと SiteFooter コンポーネントを含んでいます。
・router-viewを使って現在のルートに応じたコンポーネントを表示します。

```HomeView.vue
<script setup lang="ts">
import CardDesign from "@/components/CardDesign.vue"; 
</script>

<template>
  <div class="home">
    <h1>クリスマスカードアプリへようこそ！</h1>
    <p>あなたの大切な人に送るお気に入りのクリスマスカードを選んでカスタマイズしましょう。</p>
    <!-- CardDesignコンポーネントの呼び出し -->
    <CardDesign />
  </div>
</template>

<style>
/* 必要なスタイルを追加 */

</style>
```
役割
・アプリのホーム画面を担当
・CardDesign コンポーネントを `<CardDesign />` のように呼び出しています。これにより、このコンポーネント内で CardDesign を表示し、ユーザーがクリスマスカードを選択やカスタマイズができるようにします。

```CardDesign.vue
<!-- CardDesign.vue -->
<script setup lang="ts">
import { Card, availableCards } from "../Models/CardModel"; 
import { reactive, ref } from "vue";
// リアクティブ変数
const selectedCard = reactive({} as Card);
const senderName = ref("");
const receiverName = ref("");
const customMessage = ref("");

const selectCard = (card: Card) => {
  selectedCard.id = card.id;
  selectedCard.title = card.title;
  selectedCard.imageURL = card.imageURL;
  // 他のカード情報の代入
};
const sendCard = () => {
  // カードを送信する処理を記述
};
</script>
<template>
  <div class="card-design">
    <!-- カードの選択 -->
    <div class="card-options">
      <div
        v-for="card in availableCards"
        :key="card.id"
        @click="selectCard(card)"
        :class="{ selected: selectedCard && selectedCard.id === card.id }"
      >
        <img :src="card.imageURL" :alt="card.title" />
      </div>
    </div>

    <!-- カスタマイズ可能なメッセージ -->
    <div class="message-editor">
      <div class="input-container">
        <input
          v-model="senderName"
          placeholder="あなたの名前"
          class="custom-input"
        />
        <input
          v-model="receiverName"
          placeholder="受取人の名前"
          class="custom-input"
        />
      </div>
      <div class="input-container">
        <textarea
          v-model="customMessage"
          placeholder="メッセージを書いてください..."
          class="custom-textarea"
        >
        </textarea>
      </div>
    </div>

    <!-- 選択したカードのプレビュー -->
    <div class="card-preview" v-if="selectedCard">
      <img :src="selectedCard.imageURL" :alt="selectedCard.title" />
      <p>{{ senderName }} から {{ receiverName }} へのメッセージ:</p>
      <p>{{ customMessage }}</p>
    </div>

    <!-- 送信ボタン -->
    <button @click="sendCard" class="custom-button">送信</button>
  </div>
</template>

<style scoped>
/* カードデザインのスタイル */
/* ... */
</style>
```
役割
・reactive を使って selectedCard というリアクティブなオブジェクトを作成しています。これにより、データの変更が自動的にトリガーされ、UIに反映されます。
・ref を使用して、senderName、receiverName、customMessage のような変数を宣言しています。これらは単一の値を保持し、UI要素と双方向のデータバインディングを可能にします。

```CardModel.ts
// CardModel.ts
export interface Card {
  id: number;
  title: string;
  imageURL: string;
  // その他のカードに関するプロパティを追加
}

export const availableCards = [
  { id: 1, title: "Card 1", imageURL: require("../assets/sample1.png") },
  { id: 2, title: "Card 2", imageURL: require("../assets/sample2.png") },
  { id: 3, title: "Card 3", imageURL: require("../assets/sample3.png") },
  { id: 4, title: "Card 4", imageURL: require("../assets/sample4.png") },
  { id: 5, title: "Card 5", imageURL: require("../assets/sample5.png") },
];
```
役割
・Card インターフェースは、カードのデータ構造を定義しており、型安全性を確保します。これにより、不正なデータが入ることを防ぎ、開発時のエラーを減らすことができます。
・availableCards 配列は、アプリ内で利用可能なクリスマスカードのデータを格納しています。これにより、アプリケーション全体で同じデータを共有でき、一貫性を保つことができます。

## 実際にできたアプリ
![スクリーンショット 2023-12-18 22.46.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544275/1d651db0-b827-d88b-6ca5-532d21e2697e.png)
![スクリーンショット 2023-12-18 22.52.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544275/40c4e1c8-72dd-4402-161f-ed76fefdcbb7.png)
画像を押してメッセージを入れるとちゃんと双方向データバインディングされていることがわかりました。

## さいごに
Advent Calendarの最終日を担当させていただき、大変光栄に思います。今回の記事では、コンポーネントに焦点を当てましたが、Vueに触れるきっかけやアプリ開発にお役立ちいただければ幸いです。拙い説明でしたが、少しでも皆さんのお役に立てる情報を提供できれば嬉しいです。Advent Calendarへのご参加、ありがとうございました！