テンプレート構文レンダリング
この章では、Vue のテンプレート構文を使って JS データを HTML にレンダリングし、ブログのホームページに記事カードリストを表示する方法を学びます。
挿入式 {{ }}
挿入式は Vue で最も基本的なデータバインディングの方法です。
JS 変数の値を HTML に「挿入」します。構文は二重波括弧で変数名を囲みます。
例
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ディレクティブを使用する必要があります。
例
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 を生成できます。
例
// 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シリーズのディレクティブを使用します。
例
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-if | DOM ノードの追加/削除 | 低い(レンダリングしない) | 高い(DOM を再構築) | 条件がほとんど変わらないシーン、例:権限制御 |
| v-show | CSS display の切り替え | 高い(常にレンダリング) | 低い(スタイルのみ変更) | 頻繁に切り替えるシーン、例:タブの切り替え |
簡単な覚え方:頻繁に切り替える場合は v-show、条件が安定している場合は v-if。
実践:完全なブログホームページ
これまで学んだテンプレート構文をまとめて、完全なブログホームページコンポーネントを書きます。
例
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 データに基づいて記事カードを動的にレンダリングできるようになりました。
その他の拡張