Progressive Vue

テンプレート構文 ≒ HTML

テンプレートは 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 の属性として書きます.classid を書くのと同じ場所に書きます.

ここで注目すべきは,テンプレートの構造が前章の静的 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 一枚

関連コンテンツ

本に戻る