SvelteKitとTailwindで作るBlogCardコンポーネントの実装とデータ管理のベストプラクティス
大平 恵美
大平 恵美
2025-02-06
SvelteKitとTailwindを活用してブログカードコンポーネントを作成する方法を解説します。コンポーネントの基本構造、#each を用いた複数表示、データの管理方法、const と let の使い分け、そして投稿の編集・更新の実装方法まで詳しく紹介。適切なデータ管理を行い、メンテナンス性の高いコードを書くためのベストプラクティスを学びましょう。
あなた:

あなたはプロのフロントエンドエンジニアです。SvelteKitとTailwindで開発を行います。必要なセットアップは完了しています。 BlogCard.svelteでカードコンポーネントを作ります。初心者にもわかるよう書き方を教えてください。

ChatGPT:

初心者向けに、BlogCard.svelteの作り方をわかりやすく解説します!
このコンポーネントはブログ記事をカード形式で表示するためのものです。


📌 BlogCard.svelteの基本構造

BlogCard.svelte は、以下のような構造になっています。

  1. <script>ブロック

    • 親コンポーネント(page.svelte)から渡されるデータをexport letで受け取る。
    • title, description, imageUrl, link, tags, likes, author などのプロパティを定義。
  2. HTML部分

    • 記事のサムネイル(画像)
    • 記事のタイトルと説明文
    • タグのリスト
    • いいねボタン
    • 著者情報(アイコンと名前)
  3. Tailwind CSSを活用

    • shadow-md で影をつけ、ホバー時にhover:shadow-lg で影を強調
    • rounded-lg で角を丸く
    • text-teal-600 でリンクの色を設定

📌 BlogCard.svelte(初心者向け解説付き)

<script>
  // 親コンポーネントから受け取るデータ
  export let title = "記事のタイトル"; // 記事のタイトル
  export let description = "記事の概要がここに入ります。"; // 記事の概要
  export let imageUrl = ""; // 記事のサムネイル画像URL
  export let link = "#"; // 記事の詳細ページへのリンク
  export let tags = []; // 記事のタグ(配列)
  export let likes = 0; // いいねの数
  export let author = { // 著者情報
    name: "著者名",
    avatar: "",
    profileLink: "#",
    date: "2025年2月3日(土) 14:59"
  };
</script>

<!-- カード全体 -->
<div class="p-3 w-full h-full">
  <div class="flex flex-col p-4 rounded-lg h-full bg-white shadow-md hover:shadow-lg transition-shadow duration-300">
    
    <!-- サムネイル(画像がある場合のみ表示) -->
    {#if imageUrl}
      <div class="mb-2">
        <a href={link} target="_blank" class="block">
          <img 
            src={imageUrl} 
            alt={title} 
            class="border border-gray-300 rounded-lg p-1 shadow-sm object-cover w-full h-48"
          />
        </a>
      </div>
    {/if}

    <!-- 記事タイトル -->
    <div class="mb-2 text-sm">
      <a href={link} target="_blank" class="text-teal-600 font-semibold hover:underline">
        {title}
      </a>
    </div>

    <!-- セパレーター(仕切り線) -->
    <div class="border-b-2 border-teal-600 my-1"></div>

    <!-- 記事概要 -->
    <div class="text-xs my-2 text-gray-500">
      {description}
    </div>

    <!-- セパレーター(仕切り線) -->
    <div class="border-b-2 border-teal-600 my-1"></div>

    <!-- タグ(タグがある場合のみ表示) -->
    <div class="flex flex-wrap text-xs mb-3 gap-2">
      {#each tags as tag}
        <a href={`bloglist.html?key=${tag}`} target="_blank" class="text-green-600 hover:underline">
          #{tag}
        </a>
      {/each}
    </div>

    <!-- いいね数 -->
    <div class="text-xs text-right mb-2">
      <a href={link} target="_blank" class="text-teal-600 flex items-center justify-end gap-1">
        {likes} <i class="far fa-heart"></i>
      </a>
    </div>

    <!-- 著者情報(アイコン+名前+日付) -->
    <div class="flex justify-between items-center mt-auto">
      <div class="flex items-center">
        <a href={author.profileLink} target="_blank">
          <img 
            src={author.avatar} 
            alt={author.name} 
            class="rounded-full w-12 h-12 object-cover"
          />
        </a>
        <div class="ml-2 text-gray-500 text-xs">
          <div>{author.name}</div>
          <div>{author.date}</div>
        </div>
      </div>
      <div>
        <a href={link} target="_blank" class="text-teal-600 hover:text-teal-800">
          <i class="fas fa-arrow-right fa-2x"></i>
        </a>
      </div>
    </div>
  </div>
</div>

📌 page.svelte での使い方

このコンポーネントをpage.svelteで使うには、以下のようにデータを渡します。

<script>
    import BlogCard from '$lib/components/BlogCard.svelte';

    const blogPost = {
      title: "SvelteKitで学ぶ!ButtonコンポーネントとPropsの基本理解ガイド",
      description: "本記事では、SvelteKitで再利用可能なButtonコンポーネントを作成する方法と、Props(プロパティ)を使ったデータの受け渡しについて解説します。",
      imageUrl: "",
      link: "blog.html?bid=1735918461866",
      tags: ["SvelteKit", "Buttonコンポーネント", "Props", "export let", "デフォルト値", "再利用可能", "データバインディング", "親コンポーネント", "属性管理", "UI開発"],
      likes: 12,
      author: {
        name: "湯 鵬飛",
        avatar: "https://example.com/avatar.jpg",
        profileLink: "bloglist.html?userid=PV7gmD9aWcbT24KC022c9FAXPSI3",
        date: "2025年2月3日"
      }
    };
</script>

<BlogCard {...blogPost} />

📌 初心者向け解説

  1. コンポーネントの分離

    • BlogCard.svelteはブログ記事の情報をカードとして表示する専用コンポーネント。
    • page.svelteでは、BlogCardにデータを渡して表示する。
  2. export letの役割

    • export letを使うと、page.svelteからデータを受け取れる。
    • title, description, imageUrl などがそれに当たる。
  3. データの受け渡し

    • page.svelteblogPost のオブジェクトを作成し、 BlogCard に渡している。
    • {...blogPost} で、オブジェクトのすべてのプロパティをそのまま渡せる。

📌 まとめ

  • BlogCard.svelte でブログカードを作成
  • page.svelte でコンポーネントを呼び出し、データを渡す
  • export let を使って親からデータを受け取る
  • Tailwind CSS を使ってデザインを整える

これでSvelteKitとTailwindを使ったブログカードが完成しました! 🎉
このまま応用して、さらに機能を追加することも可能です。わからない部分があれば質問してください!🚀

あなた:

コンポーネントを6回繰り返して表示するには?

ChatGPT:

Svelteでは、#eachを使ってコンポーネントを繰り返し表示できます。
例えば、BlogCardを6回表示する場合、page.svelte で以下のように書けばOKです。


✅ 6回繰り返す方法

<script>
    import BlogCard from '$lib/components/BlogCard.svelte';

    // 6つのブログ記事データ
    const blogPosts = [
        {
            title: "SvelteKitで学ぶ!Buttonコンポーネント",
            description: "ButtonコンポーネントとPropsの基本について解説。",
            imageUrl: "https://source.unsplash.com/400x300/?tech",
            link: "blog.html?bid=1",
            tags: ["SvelteKit", "Props", "UI開発"],
            likes: 12,
            author: {
                name: "大平 恵美",
                avatar: "https://example.com/avatar1.jpg",
                profileLink: "#",
                date: "2025年2月3日"
            }
        },
        {
            title: "SvelteKitでのフォーム入力とバリデーション",
            description: "フォームのデータバインディングとバリデーションを実装する方法。",
            imageUrl: "https://source.unsplash.com/400x300/?coding",
            link: "blog.html?bid=2",
            tags: ["フォーム", "バリデーション", "SvelteKit"],
            likes: 20,
            author: {
                name: "山田 太郎",
                avatar: "https://example.com/avatar2.jpg",
                profileLink: "#",
                date: "2025年2月2日"
            }
        },
        {
            title: "SvelteKitでAPIを使ってデータ取得",
            description: "fetchを使ってAPIからデータを取得する方法。",
            imageUrl: "https://source.unsplash.com/400x300/?data",
            link: "blog.html?bid=3",
            tags: ["API", "fetch", "SvelteKit"],
            likes: 18,
            author: {
                name: "佐藤 花子",
                avatar: "https://example.com/avatar3.jpg",
                profileLink: "#",
                date: "2025年2月1日"
            }
        },
        {
            title: "SvelteKitでルーティングを理解する",
            description: "ルーティングの基本とページ遷移の仕組みを解説。",
            imageUrl: "https://source.unsplash.com/400x300/?navigation",
            link: "blog.html?bid=4",
            tags: ["ルーティング", "ページ遷移", "SvelteKit"],
            likes: 25,
            author: {
                name: "田中 次郎",
                avatar: "https://example.com/avatar4.jpg",
                profileLink: "#",
                date: "2025年1月30日"
            }
        },
        {
            title: "SvelteKitとTailwindで簡単なUIを作成",
            description: "Tailwindを活用したコンポーネントの作り方。",
            imageUrl: "https://source.unsplash.com/400x300/?ui",
            link: "blog.html?bid=5",
            tags: ["UIデザイン", "Tailwind", "SvelteKit"],
            likes: 30,
            author: {
                name: "中村 美咲",
                avatar: "https://example.com/avatar5.jpg",
                profileLink: "#",
                date: "2025年1月25日"
            }
        },
        {
            title: "SvelteKitでサーバーサイドレンダリングを学ぶ",
            description: "SSRとCSRの違い、SvelteKitのサーバーレンダリングを解説。",
            imageUrl: "https://source.unsplash.com/400x300/?server",
            link: "blog.html?bid=6",
            tags: ["SSR", "サーバーサイドレンダリング", "SvelteKit"],
            likes: 15,
            author: {
                name: "小林 健一",
                avatar: "https://example.com/avatar6.jpg",
                profileLink: "#",
                date: "2025年1月20日"
            }
        }
    ];
</script>

<!-- #each を使って6つのカードを表示 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
    {#each blogPosts as post}
        <BlogCard {...post} />
    {/each}
</div>

✅ 解説

  1. blogPosts 配列 を作成し、6つのブログ記事のデータを格納。
  2. #each 構文 を使い、blogPosts のデータを BlogCard に渡して繰り返し表示。
  3. {...post} の書き方
    • BlogCard {...post}post オブジェクトの全プロパティを BlogCard.svelte に渡す簡単な書き方。
  4. Tailwindでレイアウト調整
    • grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4
    • スマホでは1列 (grid-cols-1)、タブレットでは2列 (md:grid-cols-2)、PCでは3列 (lg:grid-cols-3) に並ぶ。

✅ まとめ

#each を使ってデータを繰り返し表示
データの受け渡しは {...post} で簡単に記述
Tailwindの grid を使ってレスポンシブなレイアウトに対応


📌 これで、6つの BlogCard が簡単に表示されます! 🎉

カスタマイズしたい場合は、データを増やしたり、表示レイアウトを変えたりするだけでOKです。 🚀

あなた:

constを使わずにコンポーネントを複数回表示させる方法は可能か。

ChatGPT:

const を使わずにコンポーネントを複数回表示する方法

const を使わずに BlogCard を複数回表示するには、以下の方法があります。


📌 方法 1: #each の中で直接オブジェクトを作成

<script>
  import BlogCard from './BlogCard.svelte';
</script>

{#each [
  {
    title: "SvelteKitで学ぶ!ButtonコンポーネントとPropsの基本理解",
    description: "SvelteKitで再利用可能なButtonコンポーネントの作成方法を紹介します。",
    imageUrl: "https://example.com/image1.jpg",
    link: "/blog/sveltekit-button-props",
    tags: ["SvelteKit", "Props", "UI開発"],
    likes: 10,
    author: {
      name: "大平恵美",
      avatar: "https://example.com/avatar1.jpg",
      profileLink: "/user/ohiramegumi",
      date: "2025年2月5日",
      time: "13:14",
    }
  },
  {
    title: "SvelteKitでのフォーム入力とバリデーション",
    description: "フォームのデータバインディングとバリデーションの方法を解説。",
    imageUrl: "https://example.com/image2.jpg",
    link: "/blog/sveltekit-form-validation",
    tags: ["フォーム", "バリデーション", "SvelteKit"],
    likes: 5,
    author: {
      name: "田中太郎",
      avatar: "https://example.com/avatar2.jpg",
      profileLink: "/user/tanakataro",
      date: "2025年2月6日",
      time: "10:30",
    }
  }
] as post}
  <BlogCard {...post} />
{/each}

📌 方法 2: #each の外にデータを定義

const を使わずに let で変数を定義する方法)

<script>
  import BlogCard from './BlogCard.svelte';

  let blogPosts = [
    {
      title: "SvelteKitで学ぶ!ButtonコンポーネントとPropsの基本理解",
      description: "SvelteKitで再利用可能なButtonコンポーネントの作成方法を紹介します。",
      imageUrl: "https://example.com/image1.jpg",
      link: "/blog/sveltekit-button-props",
      tags: ["SvelteKit", "Props", "UI開発"],
      likes: 10,
      author: {
        name: "大平恵美",
        avatar: "https://example.com/avatar1.jpg",
        profileLink: "/user/ohiramegumi",
        date: "2025年2月5日",
        time: "13:14",
      }
    },
    {
      title: "SvelteKitでのフォーム入力とバリデーション",
      description: "フォームのデータバインディングとバリデーションの方法を解説。",
      imageUrl: "https://example.com/image2.jpg",
      link: "/blog/sveltekit-form-validation",
      tags: ["フォーム", "バリデーション", "SvelteKit"],
      likes: 5,
      author: {
        name: "田中太郎",
        avatar: "https://example.com/avatar2.jpg",
        profileLink: "/user/tanakataro",
        date: "2025年2月6日",
        time: "10:30",
      }
    }
  ];
</script>

{#each blogPosts as post}
  <BlogCard {...post} />
{/each}

📌 どちらの方法が良い?

方法 メリット デメリット
方法 1 (#each 内でオブジェクト作成) const を使わずに直接書ける コードが長くなりがち
方法 2 (let を使う) 変数として管理でき、データ変更も可能 let を使う必要がある

📌 まとめ

const を使わなくても #each の中で直接オブジェクトを作成 すればOK!
✅ データを変更したい場合は let を使って blogPosts を定義 すれば、動的な更新も可能!

この方法で BlogCard複数回表示 できます 🚀✨

あなた:

サンプルコードはいらないので、直接オブジェクトを書くメリットとデメリットを教えてほしい。

ChatGPT:

📌 直接オブジェクトを書くメリットとデメリット

✅ メリット

  1. 即座に表示できる

    • 変数を宣言する必要がなく、コード量が減る。
    • #each 内で直接オブジェクトを定義できるため、シンプルに記述可能。
  2. データのスコープを限定できる

    • let などの変数を定義しないため、その場限りでデータを使用 できる。
    • 余計なメモリ使用を避け、意図しないデータ変更を防げる。
  3. 関数やAPIの結果をそのまま使える

    • fetch などのデータ取得処理を直接 #each の中に書くことで、変数の管理を省略できる。

❌ デメリット

  1. 可読性が低下する

    • #each の中にオブジェクトが長くなると、見づらくなりがち。
    • データが多いと、構造が把握しにくくなる。
  2. 再利用しにくい

    • 同じデータを複数箇所で使う場合、毎回同じオブジェクトを書かなければならない。
    • let で変数化すれば、管理しやすくなる。
  3. 動的なデータ変更が難しい

    • let を使わないので、後からデータを変更したり、APIから取得したデータを加工するのが難しい。
    • 例えば、ボタンを押してデータを更新したい場合、変数として管理するほうが適している。

📌 どちらを選ぶべきか?

静的なデータ(変更しないデータ)なら直接オブジェクト
動的にデータを変更したり、APIで取得するなら変数 (let) を使う

➡ 短くて単発なら「直接オブジェクト」、変更や管理が必要なら「変数で定義」! 🚀

あなた:

constとletの使い分けについて、constは定数、letは変数で合っていますか。varは使いますか。

ChatGPT:

constlet の使い分け

はい、const は定数、let は変数 という理解で合っています!
ただし、JavaScript の仕様上 const も「再代入不可の変数」 なので、少し補足します。


📌 const の特徴

  • 値を再代入できないconst で宣言した変数は一度代入すると変更できない)
  • オブジェクトや配列の内容は変更可能(ただし、新しいオブジェクトや配列を代入することはできない)

const の例

const name = "太郎";
name = "次郎"; // ❌ エラー: 再代入できない

const で配列・オブジェクトを操作

const user = { name: "太郎", age: 25 };
user.age = 26; // ✅ OK: オブジェクトのプロパティは変更できる

const numbers = [1, 2, 3];
numbers.push(4); // ✅ OK: 配列の中身は変更できる

user = {}; // ❌ エラー: `const` で宣言した変数に新しいオブジェクトは代入できない

📌 let の特徴

  • 値を再代入できる
  • スコープはブロック単位
  • 変数を後から変更する場合に使用

let の例

let age = 25;
age = 26; // ✅ OK: 再代入できる

let を使う場面

let count = 0;
function increment() {
  count += 1; // ✅ OK: let なので値を変更できる
}

📌 var は使うべきか?

結論: var は使わない方が良い! 🚫

var の問題点

  1. var は関数スコープで、ブロックを無視する

    if (true) {
      var x = 10;
    }
    console.log(x); // ✅ 10(ブロックの外でも使える) → 予期せぬバグの原因に!
    

    let はブロックの中だけ有効なので、こういったバグを防げる。

  2. var は再宣言できる

    var x = 10;
    var x = 20; // ✅ エラーにならない(上書きできる)
    

    letconst は同じ変数名を再宣言するとエラーになるので、バグを防げる。


📌 まとめ: const vs let vs var

宣言方法 再代入 スコープ 使うべき?
const ❌ できない ブロック ほぼ全てのケースで使うべき
let ✅ できる ブロック 変数を変更する必要がある場合のみ使う
var ✅ できる 関数 🚫 使わない(古い書き方)

📌 いつ const を使う? let を使う?

基本は const を使う
値を変更する必要がある場合だけ let を使う
var は使わない

const name = "太郎"; // 🔹 変更しないなら const
let age = 25; // 🔹 後で変更するなら let

📌 どのような場面で使う?

🟢 const を使う場面

  1. オブジェクト・配列の定義
    const user = { name: "太郎", age: 25 };
    
  2. 関数やクラスの宣言
    const greet = () => console.log("こんにちは!");
    

🟡 let を使う場面

  1. ループ処理
    for (let i = 0; i < 10; i++) {
      console.log(i);
    }
    
  2. 変数を更新する必要があるとき
    let count = 0;
    count += 1;
    

📌 これで完璧! constlet を正しく使おう! 🎯

基本は const、変更が必要なら let
var はもう使わない! 🚫
スコープ(ブロック or 関数)を意識する

const をデフォルトにし、let は必要な時だけ使おう! 🚀

あなた:

同じカードでも投稿内容が変わって更新されていきます。この場合はletを使うべきなのでは?

ChatGPT:

✅ 状況:同じカードでも投稿内容が変わる 結論:let を使うべきか? → 状況による

📌 どんな更新方法なのかを考える 投稿内容が一度決まったら変わらない(APIから取得後は固定) → const を使う ユーザーの操作でデータが変わる(新しい投稿を取得・追加) → let を使う リアルタイムでデータが変わる(WebSocketやPollingで更新) → $ ストア(writable)を使うのが最適

📌 それぞれのケースの使い分け 🟢 const を使うケース ✅ APIから取得後、一度データがセットされたら変わらない ✅ #each でループするが、新しくデータを取得しない

🟡 let を使うケース ✅ データが更新される場合(APIから取得・変更する) ✅ ボタンを押したときに新しいデータを取得する場合

🟠 writable ストアを使うケース ✅ リアルタイムで投稿データが更新される(WebSocket、Polling など) ✅ 複数のコンポーネントで同じデータを管理する場合

📌 結論:どれを使う? 状況 使うべきもの データが固定(API取得後に変更しない) const データを更新する(ボタンで取得し直す) let リアルタイムで変更される(WebSocket, Polling) writable ストア

あなた:

投稿内容を編集して更新することもあるかと思います。この場合は?

ChatGPT:

✅ 投稿内容を編集して更新する場合の let の使い方

結論:投稿を編集して更新するなら let を使うのが適切!
なぜなら、投稿データが変更される から。


📌 let を使う理由

  1. 編集されたデータを更新できる

    • const だとデータを変更できないため、編集機能には向かない。
    • let なら blogPosts の内容を変更できる。
  2. ユーザーが変更した内容を即座に反映

    • let で管理すれば、Svelte のリアクティブ機能 ($:) で自動的に UI を更新できる。

📌 例: 投稿を編集して更新する

<script>
  import BlogCard from './BlogCard.svelte';

  // ✅ let を使って投稿データを変更できるようにする
  let blogPosts = [
    {
      id: 1,
      title: "SvelteKitで学ぶ!Buttonコンポーネント",
      description: "ボタンコンポーネントとPropsの基本を解説。",
      link: "/blog/sveltekit-button-props",
      tags: ["SvelteKit", "Props"],
      likes: 10
    },
    {
      id: 2,
      title: "SvelteKitでのフォーム入力とバリデーション",
      description: "フォームのデータバインディングの方法を解説。",
      link: "/blog/sveltekit-form-validation",
      tags: ["フォーム", "バリデーション"],
      likes: 5
    }
  ];

  // ✅ 編集する投稿を管理するための変数
  let editingPost = null;
  let newTitle = "";
  let newDescription = "";

  // 投稿を編集モードにする
  function editPost(post) {
    editingPost = post;
    newTitle = post.title;
    newDescription = post.description;
  }

  // 投稿を更新する
  function updatePost() {
    if (editingPost) {
      editingPost.title = newTitle;
      editingPost.description = newDescription;
      editingPost = null; // 編集モードを終了
    }
  }
</script>

<!-- 投稿のリスト -->
{#each blogPosts as post}
  <BlogCard {...post} />
  <button on:click={() => editPost(post)}>編集</button>
{/each}

<!-- 編集フォーム(投稿が選択された場合のみ表示) -->
{#if editingPost}
  <div class="p-4 border rounded-md mt-4">
    <h2>投稿を編集</h2>
    <input type="text" bind:value={newTitle} class="border p-2 w-full" />
    <textarea bind:value={newDescription} class="border p-2 w-full mt-2"></textarea>
    <button on:click={updatePost} class="bg-blue-500 text-white px-4 py-2 mt-2">更新</button>
  </div>
{/if}

📌 仕組み

  1. let blogPosts = [...]

    • 投稿データを let で管理し、編集時に変更可能に。
  2. let editingPost = null

    • どの投稿を編集しているかを管理する変数。
  3. 編集ボタン (editPost())

    • クリックすると editingPost に現在の投稿をセットし、編集フォームを表示。
  4. 更新ボタン (updatePost())

    • editingPosttitledescription を更新。

let を使う理由

const let
投稿内容を編集する ❌ 変更できない ✅ 変更できる
ユーザーの操作でデータを更新 ❌ エラーになる ✅ 動的に変更可能
編集した内容を即時反映 const では不可 Svelte のリアクティブ機能で即時更新

🎯 let を使うべきケース

投稿の編集・更新がある
データが動的に変わる(APIから取得した新しい投稿をセットする)
リアクティブに UI を更新したい


📌 まとめ

投稿内容が編集・更新されるなら let を使うべき!
理由: let なら Svelte のリアクティブ機能を活かして UI を自動更新できる! 🚀✨