Template Syntax and Interpolation
Mustache interpolation, v-bind for attributes, and how Vue templates differ from plain HTML.
読了時間 2 分
Vue templates are HTML with a small set of extensions for inserting dynamic data and reacting to it. The most basic one is text interpolation, using double curly braces (often called "mustaches"):
<script setup>
const username = "Ada";
const loginCount = 12;
</script>
<template>
<p>Welcome back, {{ username }}. You've logged in {{ loginCount }} times.</p>
<p>Next login count: {{ loginCount + 1 }}</p>
</template>Anything inside {{ }} is a JavaScript expression — it can reference variables from <script setup>, do arithmetic, call simple functions, or use the ternary operator. It can't contain statements (no if, no for, no assignments) — for conditional and repeated content, Vue has dedicated directives (v-if, v-for) covered in later lessons, precisely because expressions alone can't express them.
Binding attributes with v-bind
Mustaches only work inside text content — they don't work inside an HTML attribute:
<!-- This does NOT work -->
<img src="{{ imageUrl }}" />For attributes, use the v-bind directive, usually written with its shorthand ::
<script setup>
const imageUrl = "/logo.png";
const isDisabled = true;
</script>
<template>
<img v-bind:src="imageUrl" alt="Logo" />
<img :src="imageUrl" alt="Logo (shorthand)" />
<button :disabled="isDisabled">Submit</button>
</template>:disabled="isDisabled" is worth pausing on: the value on the right is a JavaScript expression, not a string. When isDisabled is true, Vue adds the disabled attribute; when it's false, Vue removes it entirely, rather than writing the literal text "false" into the HTML (which browsers would still treat as disabled, since HTML boolean attributes are true whenever they're present at all).
Dynamic classes and styles
:class and :style get special handling that makes them easier to work with than plain string concatenation:
<script setup>
import { ref } from "vue";
const isActive = ref(true);
const hasError = ref(false);
</script>
<template>
<div :class="{ active: isActive, error: hasError }">Status card</div>
<div :style="{ color: isActive ? 'green' : 'gray', fontWeight: 'bold' }">Label</div>
</template>The object form of :class toggles each key's class name on or off based on whether its value is truthy — no manual string building like `card ${isActive ? "active" : ""}`. :style takes a plain JS object with camelCased CSS properties, which Vue converts to the equivalent inline style attribute.
Why this split exists
Text content, attributes, and directives each have different syntax because they answer different questions: "what text goes here," "what value does this attribute have," and "how should Vue treat this element structurally." Keeping them visually distinct ({{ }} vs :attr vs v-directive) makes it possible to glance at a template and know which kind of binding you're looking at, even before reading the expression itself.