テンプレートはHTMLである
Vue.jsのテンプレート構文を学ぶにあたって,最も重要なことを先に言います.
Vueのテンプレートは,validなHTMLです.
JSXのようにJavaScriptの中にHTMLを書くのではありません.
HTMLの中に,ほんの少しだけVue固有の属性を足すだけです.
ブラウザのHTMLパーサーがそのまま解釈できる,正当なHTMLです.
これはVueの設計思想の根幹にある考え方です.
Mustache構文: {{ }}
データをテンプレートに表示するには {{ }} (Mustache構文) を使います.
まずは一番シンプルな例から.
<div id="app">
<h1>{{ message }}</h1>
</div>
<script type="importmap">
{ "imports": { "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js" } }
</script>
<script type="module">
import { createApp, ref } from "vue";
createApp({
setup() {
const message = ref("My Bookshelf");
return { message };
},
}).mount("#app");
</script>
{{ message }} の部分がsetup() で定義したmessageの値に置き換わります.
テンプレートの残りは ただのHTMLのままです.
v-for: 繰り返し
HTMLをコピペする代わりに,配列からリストを生成します.
<div id="app">
<h1>My Bookshelf</h1>
<p>最近読んだ本の記録です.</p>
<div class="card" v-for="book in books">
<h2>{{ book.title }}</h2>
<p>{{ book.description }}</p>
<span class="tag" v-for="tag in book.tags"> {{ tag }} </span>
</div>
</div>
v-for="book in books" — これがVueのディレクティブです.
HTMLの属性として書きます.classやidを書くのと同じ場所に書きます.
ここで注目すべきは,テンプレートの構造が前章の静的HTMLとほぼ同じ だということです.
| 静的HTML | Vueテンプレート |
|---|---|
<div class="card"> |
<div class="card" v-for="book in books"> |
<h2>リーダブルコード</h2> |
<h2>{{ book.title }}</h2> |
<span class="tag">programming</span> |
<span class="tag" v-for="tag in book.tags">{{ tag }}</span> |
HTMLの構造はそのまま.属性が一つ増えて,テキストが {{ }} になっただけ.
これがテンプレートDSL (Domain Specific Language)です.HTMLを壊さず,拡張しています.
v-bind: 属性のバインディング
テキストコンテンツだけでなく,HTML属性にもデータを反映できます.
<a v-bind:href="book.url">{{ book.title }}</a>
省略記法として : だけで書くこともできます.
<a :href="book.url">{{ book.title }}</a>
これもHTMLの属性 の位置に書いています.
特殊な構文ではなく,HTMLの属性の値にデータをバインドしているだけです.
v-if / v-else: 条件付きレンダリング
<div class="card" v-for="book in books">
<h2>{{ book.title }}</h2>
<p>{{ book.description }}</p>
<p v-if="book.tags.length === 0">タグなし</p>
<div v-else>
<span class="tag" v-for="tag in book.tags"> {{ tag }} </span>
</div>
</div>
v-ifもHTMLの属性です.hidden属性を書くような感覚で使えます.
完全なコード
ここまでの内容をまとめた完全なコードです.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
<style>
body {
font-family: sans-serif;
max-width: 600px;
margin: 40px auto;
padding: 0 20px;
}
.card {
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
}
.card h2 {
margin-top: 0;
}
.tag {
display: inline-block;
background: #edf2f7;
border-radius: 4px;
padding: 2px 8px;
font-size: 0.85em;
margin-right: 4px;
}
</style>
</head>
<body>
<div id="app">
<h1>My Bookshelf</h1>
<p>最近読んだ本の記録です.</p>
<div class="card" v-for="book in books">
<h2>{{ book.title }}</h2>
<p>{{ book.description }}</p>
<span class="tag" v-for="tag in book.tags"> {{ tag }} </span>
</div>
</div>
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
}
}
</script>
<script type="module">
import { createApp, ref } from "vue";
createApp({
setup() {
const books = ref([
{
title: "リーダブルコード",
description: "読みやすいコードを書くための実践的な技法.",
tags: ["programming", "practices"],
},
{
title: "プログラミング言語の基礎概念",
description: "言語の設計を支える根本的な概念を学ぶ.",
tags: ["cs", "language"],
},
{
title: "Web ブラウザセキュリティ",
description: "ブラウザに関するセキュリティを体系的に解説.",
tags: ["security", "browser"],
},
]);
return { books };
},
}).mount("#app");
</script>
</body>
</html>
ブラウザで開くと,前章と まったく同じ見た目 です.
しかし裏側では,データとビューが分離されています.
本を追加したければ,books配列にオブジェクトを足すだけです.
テンプレートは「別言語」ではない
ReactのJSXはJavaScriptの拡張です.書くためにはBabelなどのトランスパイラが必要です.
Angularのテンプレートは独自の構文が多く,HTMLからはかなり離れています.
VueのテンプレートはHTMLそのもの に,いくつかの属性(v-for, v-if, v-bind)と {{ }} を足しただけです.
HTMLを書ける人なら,すぐに読める
ブラウザのDevToolsでそのまま見える
HTMLのバリデーターも(ほぼ)通る
テンプレートがHTMLであること — これはVueの最大の強みの一つです.
まとめ
VueのテンプレートはvalidなHTMLにディレクティブを足したもの
{{ }}でデータを表示するv-forでリストを生成する(HTML属性として書く)v-bind(:) で属性にデータをバインドするv-if/v-elseで条件付き表示をするテンプレートの構造は 静的HTMLとほぼ同じ まま
ファイルは依然として
index.html一枚