テンプレート構文レンダリング

この章では、Vue のテンプレート構文を使って JS データを HTML にレンダリングし、ブログのホームページに記事カードリストを表示する方法を学びます。


挿入式 {{ }}

挿入式は Vue で最も基本的なデータバインディングの方法です。

JS 変数の値を HTML に「挿入」します。構文は二重波括弧で変数名を囲みます。

例

<script setup>
const blogTitle = 'サンプル フロントエンドノート'
const author = '小明'
</script>

<template>
  <h1>{{ blogTitle }}</h1>
  <p>作者:{{ author }}</p>
  <p>現在時刻:{{ new Date().toLocaleDateString() }}</p>
</template>

ページには次のようにレンダリングされます:

サンプル 前端笔记
作者:小明
当前时间:2024/5/15

波括弧内には任意の JS 式を記述できます:変数、演算、三項演算式、関数呼び出しなどが可能です。ただし、文(ステートメント)は記述できません(例:if、for)。

挿入式はタグ内部のテキストのみを置き換え、タグ自体は置き換えません。例えば

{{ content }}
の
タグ自体は影響を受けません。


属性バインディング v-bind(省略形 :)

挿入式はタグの内容にのみ使用でき、HTML 属性には使用できません。

属性で JS 変数を使用するには、次のv-bindディレクティブを使用する必要があります。

例

<script setup>
const coverUrl = '/images/vue-cover.png'
const linkUrl = '/post/1'
const isActive = true
</script>

<template>
  <!-- 完全な書き方:v-bind:属性名="変数" -->
  <img v-bind:src="coverUrl" v-bind:alt="記事のカバー画像">

  <!-- 省略形:直接 :属性名="変数" を使用 -->
  <a :href="linkUrl">続きを読む</a>

  <!-- 動的 class:オブジェクト構文を使用 -->
  <div :class="{ active: isActive, 'text-bold': true }">
このテキストには active と text-bold の2つのクラスがあります
  </div>
</template>

省略形のコロンは最も一般的な書き方で、実際の開発ではほとんどv-bind:完全な名前は使いません。


リストレンダリング v-for

ブログのホームページでは複数の記事を表示する必要があります。1つの記事は1つのオブジェクトであり、複数の記事はオブジェクトの配列です。

v-for配列をループして、各要素に対応する HTML を生成できます。

例

<script setup>
// JS オブジェクト配列で記事データをシミュレート(後で JSON ファイルから読み込むように変更)
const articles = [
  {
    id: 1,
title: 'Vue3 入門完全ガイド',
summary: 'ゼロから Vue3 コンポジション API を学び、ref、reactive、computed などの核心概念をカバーします。',
    date: '2024-05-10',
    category: 'Vue',
    cover: '/images/vue.png'
  },
  {
    id: 2,
title: 'JavaScript 非同期プログラミング詳解',
summary: 'Promise、async/await、イベントループとマイクロタスクキューを1つの記事で完全に理解します。',
    date: '2024-05-08',
    category: 'JavaScript',
    cover: '/images/js.png'
  },
  {
    id: 3,
title: 'CSS Grid レイアウト実践',
summary: 'CSS Grid を使って複雑なレスポンシブレイアウトを簡単に実現し、float と flexbox の限界に別れを告げます。',
    date: '2024-05-05',
    category: 'CSS',
    cover: '/images/css.png'
  }
]
</script>

<template>
  <div class="article-list">
    <!-- v-for="各項目 in 配列" :key="一意の識別子" -->
    <div v-for="article in articles" :key="article.id" class="card">
      <img :src="article.cover" :alt="article.title">
      <div class="card-body">
        <span class="category">{{ article.category }}</span>
        <h3>{{ article.title }}</h3>
        <p>{{ article.summary }}</p>
        <span class="date">{{ article.date }}</span>
      </div>
    </div>
  </div>
</template>

<style scoped>
.article-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
  margin-top: 20px;
}

.card {
  background: #fff;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0,0,0,0.08);
  transition: transform 0.2s;
}

.card:hover {
  transform: translateY(-4px);
}

.card img {
  width: 100%;
  height: 180px;
  object-fit: cover;
}

.card-body {
  padding: 16px;
}

.category {
  display: inline-block;
  padding: 2px 10px;
  background: #E8EEF5;
  color: #427DB8;
  border-radius: 12px;
  font-size: 12px;
}

.card-body h3 {
  margin: 10px 0 8px;
  font-size: 18px;
}

.card-body p {
  color: #666;
  font-size: 14px;
  line-height: 1.6;
}

.date {
  display: block;
  margin-top: 10px;
  font-size: 12px;
  color: #999;
}
</style>

key 属性の重要性

:keyは v-for の必須パートナーであり、各要素の身元を Vue に伝えます。

配列の順序が変わるとき(挿入、削除、並べ替えなど)、Vue は key を通じてどの DOM を更新すべきか、どれを再利用できるかを判断します。

key の値結果
一意の id(推奨)各要素を正しく追跡し、効率的に更新
配列のインデックス index挿入/削除時に状態が混乱する可能性があります(例:入力欄の内容が入れ替わる)
key を書かないコンソールに警告が表示され、Vue はデフォルトで「インプレース更新」戦略を使用するため、パフォーマンスが低下します。

常に v-for に一意で安定した key を付け、index を key として使わないでください。これは Vue 開発における最も重要な習慣の1つです。


条件付きレンダリング v-if / v-else

異なる条件で異なる内容を表示する必要がある場合は、v-ifシリーズのディレクティブを使用します。

例

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

const articles = ref([]) // 記事リスト(空配列はデータなしを意味)
const isLoading = ref(true) // ロード状態
// データ読み込みをシミュレート:2秒後にデータを投入
setTimeout(() => {
  articles.value = [
{ id: 1, title: 'Vue3 入門', summary: '...' },
{ id: 2, title: 'React 比較', summary: '...' }
  ]
  isLoading.value = false
}, 2000)
</script>

<template>
  <div>
    <!-- 読み込み中 -->
    <p v-if="isLoading">読み込み中、しばらくお待ちください...</p>

    <!-- データがある場合はリストを表示 -->
    <div v-else-if="articles.length > 0">
      <div v-for="article in articles" :key="article.id" class="card">
        <h3>{{ article.title }}</h3>
        <p>{{ article.summary }}</p>
      </div>
    </div>

    <!-- データがない場合は空の状態を表示 -->
    <p v-else>まだ記事がありません。お楽しみに。</p>
  </div>
</template>

このパターンは UI で最も一般的な3つの状態をカバーしています:読み込み中 → データあり → データなし。

v-if vs v-show

ディレクティブ切り替え方法初期レンダリングコスト切り替えコスト適用シーン
v-ifDOM ノードの追加/削除低い(レンダリングしない)高い(DOM を再構築)条件がほとんど変わらないシーン、例:権限制御
v-showCSS display の切り替え高い(常にレンダリング)低い(スタイルのみ変更)頻繁に切り替えるシーン、例:タブの切り替え

簡単な覚え方:頻繁に切り替える場合は v-show、条件が安定している場合は v-if。


実践:完全なブログホームページ

これまで学んだテンプレート構文をまとめて、完全なブログホームページコンポーネントを書きます。

例

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

// 記事データ(現在はハードコード、後の章で fetch による読み込みに変更)
const articles = ref([
  {
    id: 1,
title: 'Vue3 入門完全ガイド',
summary: 'ゼロから Vue3 コンポジション API を学び、ref、reactive、computed などの核心概念をカバーします。',
    date: '2024-05-10',
    category: 'Vue',
    cover: '/images/vue.png'
  },
  {
    id: 2,
title: 'JavaScript 非同期プログラミング詳解',
summary: 'Promise、async/await、イベントループとマイクロタスクキューを1つの記事で完全に理解します。',
    date: '2024-05-08',
    category: 'JavaScript',
    cover: '/images/js.png'
  },
  {
    id: 3,
title: 'CSS Grid レイアウト実践',
summary: 'CSS Grid を使って複雑なレスポンシブレイアウトを簡単に実現します。',
    date: '2024-05-05',
    category: 'CSS',
    cover: '/images/css.png'
  }
])
</script>

<template>
  <div class="home">
    <h2 class="section-title">最新の記事</h2>

    <!-- 読み込み中 -->
    <p v-if="!articles.length" class="empty-tip">まだ記事がありません。お楽しみに。</p>

    <!-- 記事カードリスト -->
    <div v-else class="article-grid">
      <div
       v-for="article in articles"
       :key="article.id"
       class="article-card"
     >
        <img :src="article.cover" :alt="article.title" class="card-cover">
        <div class="card-content">
          <span class="card-category">{{ article.category }}</span>
          <h3 class="card-title">{{ article.title }}</h3>
          <p class="card-summary">{{ article.summary }}</p>
          <span class="card-date">{{ article.date }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.home {
  max-width: 960px;
  margin: 0 auto;
  padding: 20px;
}

.section-title {
  font-size: 24px;
  margin-bottom: 20px;
  color: #333;
}

.article-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 24px;
}

.article-card {
  background: #fff;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0,0,0,0.08);
  transition: transform 0.2s, box-shadow 0.2s;
  cursor: pointer;
}

.article-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}

.card-cover {
  width: 100%;
  height: 180px;
  object-fit: cover;
}

.card-content {
  padding: 16px;
}

.card-category {
  display: inline-block;
  padding: 2px 10px;
  background: #E8EEF5;
  color: #427DB8;
  border-radius: 12px;
  font-size: 12px;
  margin-bottom: 8px;
}

.card-title {
  font-size: 18px;
  margin-bottom: 8px;
  color: #222;
}

.card-summary {
  font-size: 14px;
  color: #666;
  line-height: 1.6;
  margin-bottom: 12px;
}

.card-date {
  font-size: 12px;
  color: #999;
}

.empty-tip {
  text-align: center;
  color: #999;
  padding: 60px 0;
  font-size: 16px;
}
</style>

本章のまとめ

この章では4つの核心的なテンプレート構文を学びました:{{ }}挿入式でデータを表示、v-bind(省略形:)で属性をバインド、v-forループでリストをレンダリング、v-if/v-else条件で表示/非表示を制御。

これで、ブログのホームページは JS データに基づいて記事カードを動的にレンダリングできるようになりました。

その他の拡張