Vue — Template 補間
テクノロジ系 / L01 template-syntax — テンプレート構文
Vue — Template 補間
{{ }} でデータを画面に映す
この章を終えると、できること
{{ }}(マスタッシュ)を使って、JavaScript のデータを画面に表示できる{{ }}の中に 計算や呼び出し(式) を書いて、結果を表示できる- データを書き換えると 画面が自動で変わる(リアクティブ)感覚をつかむ
> 完成イメージ:const name = "Vue" と書いたデータが、
> 画面に Hello, Vue と出る。name を変えれば表示も変わる。
この一点だけに集中します。属性(:src など)やボタンの本格的な扱いは次章以降。
なぜ学ぶ? — 「値を直接書く」と何が困るか
例:見出しに名前を出したい。素の HTML なら、こう書く 👇
<h1>Hello, Taro</h1>
一見これで十分。でも、こんなときに困ります。
- 名前が 後で変わる(ログインしたユーザーごとに違う)
- 同じ値を何度も使う(あちこちに名前を出す)
- 値を 計算したい(合計金額、件数 …)
Taro を直接書いてしまうと、変わるたびに HTML を手で書き直すことになります。
解決策:値は「データ」に置き、画面には差し込む
直接書く代わりに、値を変数(データ)に入れて、画面には {{ }} で差し込みます。
<script setup lang="ts">
const name = "Taro"; // ← 値はここに 1 か所だけ
</script>
<template>
<h1>Hello, {{ name }}</h1> <!-- ← ここに差し込まれる -->
</template>> 📺 画面の表示:Hello, Taro
name を "Hanako" に変えれば、画面は Hello, Hanako に変わります。
値は 1 か所、表示はそこを見るだけ。 これが補間(ほかん)です。
いま出てきた .vue ファイルの正体
Vue では、見た目とデータを 1 つのファイルにまとめて書きます。
このファイルを SFC(Single File Component) と呼びます(=部品 1 つ=1 ファイル)。
<script setup lang="ts"> <!-- ① データやロジック(JavaScript) -->
const name = "Taro";
</script>
<template> <!-- ② 画面(HTML) -->
<h1>Hello, {{ name }}</h1>
</template>
<style scoped> <!-- ③ 見た目(CSS)。この章では飾り、気にしなくてOK -->
h1 { color: blue; }
</style>> 🔰 今は <script setup> と <template> の 2 つだけ見れば十分。
> lang="ts"(TypeScript)や scoped(この部品だけに効く CSS)は飾り。後で学びます。
図解:データが画面に届くまで
<script setup> に書いた変数は、同じファイルの <template> からそのまま使えます。
(return や受け渡しの記述は不要。Vue が自動でつないでくれる)
flowchart LR A["script setup<br/>const name = 'Taro'"] -->|名前で参照| B["template<br/>マスタッシュで name を参照"] B -->|Vue が置き換え| C["画面<br/>Hello, Taro"]
> 💡 ポイント:{{ name }} は「ここに name の中身を入れて」という目印。
> Vue がその目印を、name の値(Taro)に置き換えて画面に出します。
まず動かして確かめよう(独学者向け)
頭で追うより、動かすのが一番速いです。L00 で作った環境を使います。
src/App.vueを開き、中身を上の SFC 例で丸ごと置き換える- ターミナルで開発サーバを起動:
npm run dev
- 表示された URL(例
http://localhost:5173)をブラウザで開く - 画面に Hello, Taro が出れば成功 ✅
> 🧪 確認のコツ:name の値を書き換えて保存すると、
> ブラウザが自動で更新され、表示も変わります。これを何度か試すと感覚がつかめます。
{{ }} の中身は「式」 — 計算や呼び出しもできる
{{ }} には変数名だけでなく、JavaScript の式が書けます。
<template>
<p>{{ message }}</p> <!-- 変数そのまま -->
<p>{{ count + 1 }}</p> <!-- 足し算 -->
<p>{{ user.name.toUpperCase() }}</p> <!-- メソッド呼び出し -->
<p>{{ items.length }} 件</p> <!-- プロパティ参照 -->
<p>{{ isAdmin ? "管理者" : "一般" }}</p> <!-- 三項演算子 -->
</template>> どれも「計算した結果の値」が画面に出ます。
> count が 4 なら count + 1 は 5 と表示。
> 📋 これ以降のコードは説明用の <template> 抜粋です。そのまま貼っても
> message is not defined で動きません。動かすときは <script setup> 側に
> const message = "..." のように使う変数を自分で用意してください。
つまずき先回り:「式」って何?「文」とどう違う?
{{ }} の中に書けるのは 式(しき)だけ。文(ぶん)は書けません。
ここが最初の関門なので、見分け方をはっきりさせます。
| 正体 | 例 | {{ }} の中 | |
|---|---|---|---|
| 式 | 計算すると値が 1 個残るもの | 1 + 2(→ 3), name.toUpperCase() | ✅ 書ける |
| 文 | 「〜せよ」という命令 | if (a) {...}, const x = 1, for (...) | ❌ 書けない |
> 🔑 見分け方:そのコードを「= の右側に置けるか?」
> const r = (1 + 2) は OK → 式。const r = (if ...) はおかしい → 文。
{{ }} は「最後に値が 1 個残る場所」とだけ覚えればOK。
「式は1つだけ」 — OK と NG
{{ }} に入れられるのは、値が 1 個に決まる1 つの式だけです。
<template>
<p>{{ a + b }}</p> <!-- ✅ 値が1個 -->
<p>{{ items.filter(x => x > 0).length }}</p> <!-- ✅ つながっても結果は1個 -->
<p>{{ let x = 1; x + 1 }}</p> <!-- ❌ 変数宣言は「文」 -->
<p>{{ if (a) "yes" }}</p> <!-- ❌ if は「文」 -->
</template>> ⚠️ NG が出たら「これは命令(文)を書いていないか?」を疑う。
> 計算が複雑になりすぎたら、<script> 側で計算してから {{ }} で呼びます
> (その専用の仕組み computed は ch01 の後半/別レッスンで学びます)。
おまけ:v-text は {{ }} と同じ意味
同じ「値を差し込む」書き方に v-text もあります。意味は {{ }} と同じです。
<p>{{ message }}</p> <!-- これと -->
<p v-text="message"></p> <!-- これは同じ結果 -->> 🔰 迷ったら {{ }} を使えばOK。 普段は {{ }} だけで困りません。
> v-text は「タグの中身を丸ごと差し替えたい」特殊な場面で使う、と頭の隅に置く程度で十分。
⚠️ 関連して、v-html(文字列を HTML として描き込む)という危ないものもあります。
他人が用意した文字列に使うと攻撃される危険(XSS)があるため、専用の安全な章で改めて扱います。
つまずき先回り:{{ }} はタグの中身専用
{{ }} が使えるのは、タグとタグに挟まれた中身(テキスト)だけです。
属性(src や href など)の中には書けません。
<template>
<h1>{{ title }}</h1> <!-- ✅ 中身ならOK -->
<img src="{{ imageUrl }}" /> <!-- ❌ 属性には書けない -->
</template>> 🧯 src="{{ imageUrl }}" と書くと、{{imageUrl}} という文字そのままが
> URL として扱われ、画像は出ません(壊れた画像になる)。
属性に変数を入れたいときは、{{ }} ではなくコロン : を付けます👇
属性に変数を入れたいとき(予告)
属性は別ルール。:属性名="式" と書きます(この : を v-bind と呼びます)。
<template> <img :src="imageUrl" :alt="imageAlt" /> <!-- ✅ 属性はコロン --> </template>
flowchart LR
A["タグの<b>中身</b>に値を入れる"] --> M["{{ }} マスタッシュ"]
B["<b>属性</b>に値を入れる"] --> C[": コロン (v-bind)"]> 📍 住み分け:中身は {{ }}、属性は :。
> :src や :class・:style の詳しい使い方は次章 ch02-v-bind で扱います。
> この章では「属性のときはコロン」と知っておけば十分です。
値が変わると画面も変わる = リアクティブ
ここまでの const name = "Taro" は変わらない値でした。
「後で値を変えて、画面にも反映したい」ときは、値を ref() で包みます。
<script setup lang="ts">
import { ref } from "vue";
const count = ref(0); // ← ref で包むと「変化を見張れる」データになる
function increment() {
count.value++; // ← script 側で書き換えるときは .value が必要
}
</script>
<template>
<p>カウント: {{ count }}</p> <!-- テンプレートでは .value 不要(Vue が補う) -->
</template>> 🔁 ref で包んだ値を後から書き換えると、Vue が画面を自動で更新します。
> これを リアクティブ と呼びます。
>
> ⚖️ .value の使い分け:<script> の中で読み書きするときは count.value、
> <template> の中では count だけ(.value 不要)。最初は混乱しやすいので、
> 「script 側は .value 付き、画面側は付けない」と覚えておけば十分です。
> ref の詳しい仕組みは後の章で学びます。
動く実感:押すと増えるカウンター
ref の嬉しさが一番わかる最小デモです(ボタンの @click は次章 ch03 で詳しく)。
<script setup lang="ts">
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<p>{{ count }}</p>
<button @click="count++">+1</button> <!-- 押すと count が増える -->
</template>> 🧪 npm run dev で開いてボタンを押すと、{{ count }} の数字がその場で増えます。
> count を ref でなく const count = 0 にすると…増えても画面は変わりません。
> この違いこそが「リアクティブ」。手元で両方試すと腹落ちします。
> 🤔 「あれ、count++ って文じゃないの?さっき {{ }} の中は式だけって…」
> 鋭い疑問です。ルールが違う場所だから OK なのです。
> 「式だけ」の制限は {{ }} の中だけの話。@click="..."(イベント)の中は
> 別ルールで、count++ のような代入や命令も書けます(詳細は次章 ch03)。
ボタンを押してから画面が変わるまで
「クリックしたら勝手に画面が変わる」と書きましたが、内部では順番に4ステップが起きています。時系列で見ると腹落ちします。
sequenceDiagram actor ユーザー participant ボタン as ボタン (template) participant データ as count (ref) participant 画面 as 画面表示 ユーザー->>ボタン: ① クリック ボタン->>データ: ② count++ で値を +1 データ->>画面: ③ 変化を検知 (リアクティブ) 画面-->>ユーザー: ④ 新しい数字を表示
> 💡 あなたが書くのは ② の @click="count++" だけ。③④ の「変化を検知して画面を描き直す」は Vue が自動でやってくれます。これがフレームワークを使う嬉しさです。
まとめ — この章でできるようになったこと
- ✅
{{ }}(マスタッシュ)で、データを画面の中身に差し込める - ✅
{{ }}の中には 式(値が1個に決まるもの)が書ける。文(命令)はNG - ✅
<script setup>の変数は、同じファイルの<template>からそのまま使える - ✅ 属性に入れるときは
{{ }}でなく コロン:(詳細は次章) - ✅ 後で変える値は
refで包む → 変えると画面も自動更新(リアクティブ)
flowchart LR D["データ<br/>(script setup)"] -->|マスタッシュで差し込み| V["画面 (template)"] D -. "ref なら値の変化で自動更新" .-> V
> 🚪 次は ch02-v-bind:属性に値を入れる :(v-bind)と、:class/:style を本格的に。
> その後 ch03 でボタンの @click(イベント)を詳しく扱います。
考えてみよう
既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。
Lv1: 基本 — マスタッシュ
問題
<script setup lang="ts">
const name = "Taro";
const age = 20;
const items = ["apple", "banana"];
</script>
<template>
<h1>{{ name }} ({{ age }}歳)</h1>
<p>{{ name.toUpperCase() }}</p>
<p>{{ age * 2 }}</p>
<p>{{ items.length }} items</p>
<p>{{ items.join(", ") }}</p>
</template>合格基準
5行全部期待通りに描画。
Lv2: 応用 — class / style バインディング
問題
<script setup lang="ts">
const isActive = ref(true);
const hasError = ref(false);
const color = ref("red");
</script>
<template>
<div :class="{ active: isActive, error: hasError }">Class object</div>
<div :class="[isActive ? 'active' : '', hasError ? 'error' : '']">Class array</div>
<div :style="{ color, fontSize: '20px' }">Style</div>
<button @click="isActive = !isActive">Toggle</button>
<button @click="color = color === 'red' ? 'blue' : 'red'">Color</button>
</template>合格基準
- class / style バインディングが切り替わる
- 2ボタンで動的に反映
Lv3: 発展 — v-html の危険性
問題
<script setup>
const userInput = ref("<img src=x onerror=alert('XSS') />");
</script>
<template>
<p>{{ userInput }}</p> <!-- ✅ 文字列として描画 -->
<p v-html="userInput"></p> <!-- ❌ alert が走る -->
</template>両方試して、v-html で XSS が走ることを観察。
「v-html は 信頼できるソースだけ」を 100字で説明。
合格基準
- {{ }} は安全、v-html は危険、を体感
- ルールを言語化
提出
submission.md に3問の回答を。