frontend / vue / 18_template_syntax_and_directives.md

Template syntax and directives

5 interview angles 4 min read source

Template syntax and directives

Vue’s templates are compiled, not interpreted. Everything here becomes a render function at build time, which is why the compiler can optimise around directives in ways JSX cannot. See 19_compiler_and_rendering.md.

The core directives

Directive Does
v-if / v-else-if / v-else conditionally create or destroy the element
v-show always render; toggle display: none
v-for render a list
v-bind (:) bind an attribute or prop to an expression
v-on (@) attach an event handler
v-model two-way binding
v-html set innerHTML
v-once render once and never update
v-memo skip updating a subtree unless dependencies change

v-if versus v-show

v-if unmounts: the component is destroyed, its state is lost, and onUnmounted runs. v-show keeps it in the DOM with display: none, so state and scroll position survive and toggling is cheap.

Use v-if when the branch is rarely taken or expensive to keep alive; v-show when it toggles frequently, such as a tab or a dropdown. v-show also does not work on <template> and does not respect the element’s own display rules if you fight it with CSS.

v-for

<li v-for="item in items" :key="item.id">{{ item.name }}</li>

The key requirement is the same story as React’s: it gives each node a stable identity so the patch algorithm can move rather than rebuild, preserving DOM state. Index keys corrupt state on insert, delete or reorder. See ../05_react/key_property.md — the reasoning transfers exactly.

Two Vue-specific points:

  • Never put v-if and v-for on the same element. v-if has higher priority in Vue 3, so it evaluates before the loop variable exists — you get an error rather than the filtering you wanted. Filter in a computed, or wrap in a <template v-for> with the v-if inside.
  • v-for over an object iterates (value, key, index); over a number it iterates 1..n, which is occasionally handy for pagination controls.

Modifiers

Modifiers are the part that has no React equivalent, and interviewers use them to check whether you have actually written Vue.

<form @submit.prevent="save">
  <input v-model.trim.lazy="name" @keyup.enter="save" />
  <div @click.self="close" @scroll.passive="onScroll">
Modifier Effect
.prevent / .stop preventDefault() / stopPropagation()
.self fire only when the target is this element, not a child
.once remove the listener after the first call
.capture / .passive listener options; .passive is the scroll-performance one
.enter, .esc, .ctrl key and system-modifier filters
v-model.number / .trim / .lazy coerce to number, trim whitespace, sync on change instead of input

v-model.number matters more than it looks: an <input type="number"> still produces a string, so a numeric comparison downstream fails silently without it.

v-html and security

v-html sets innerHTML and executes nothing that Vue sanitises — because Vue does not sanitise. Rendering user-supplied HTML through it is a direct XSS vector. Sanitise server-side, or with DOMPurify, or do not use it. Interpolation ({{ }}) is always escaped and is safe.

Dynamic attribute and event names (:[key]="v", @[evt]="h") built from user input are the same class of problem.

Custom directives

For low-level DOM access that a component cannot express: focus management, intersection observers, click-outside, tooltips attached to arbitrary elements.

const vFocus = {
  mounted: (el) => el.focus(),
};
// in <script setup>, a `vFoo` const is usable as v-foo in the template

Hooks are created, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, unmounted. The rule of thumb: reach for a component first, and a directive only when the behaviour must attach to an element you do not own. See 12_advanced_features.md.

Interview angle

  • v-if or v-show?” - v-if destroys and recreates, so state is lost and initial cost is avoided; v-show only toggles CSS, so toggling is cheap and state survives. Frequent toggles favour v-show; rarely-shown expensive content favours v-if.
  • “Why can’t you use v-if and v-for together?” - in Vue 3, v-if has the higher priority and runs before the loop binding exists, so it cannot see the item. Filter in a computed property instead, which is also faster because the filtering happens once rather than per item per render.
  • “Why does v-for need a key?” - stable identity for the patch algorithm, so nodes move instead of being rebuilt and DOM state stays attached to the right item. Index keys break on insert, delete and reorder.
  • “What is the risk with v-html?” - XSS. Vue escapes interpolation but v-html is raw innerHTML. Never point it at user input without sanitising.
  • “When would you write a custom directive?” - when behaviour must attach to an arbitrary element rather than live inside a component: autofocus, click-outside, lazy-loading images, tooltip positioning.