ディレクティブ・リファレンス
ichigo.js のテンプレートで使えるディレクティブを、修飾子と使用例つきで網羅します。文法は Vue ライクなので、Vue 経験者ならそのまま読み進められます。
ディレクティブは要素の属性として記述します。v- で始まるものに加えて、よく使う 2 つには短縮記法があります。
| 記法 | 短縮 | 意味 |
|---|---|---|
v-bind:href="url" |
:href="url" |
属性バインド |
v-on:click="fn" |
@click="fn" |
イベントリスナ |
実際に動くサンプルは「サンプル集」から確認できます。
テキスト補間 {{ }}
二重中括弧で式を埋め込みます。式の結果はテキストとして挿入され、HTML はエスケープされます。
<p>こんにちは、{{ user.name }} さん({{ items.length }} 件)</p>
<p>合計: {{ (price * quantity).toLocaleString() }} 円</p>
補間内には JavaScript の式を書けます(文は不可)。data / computed / methods、および $refs などのヘルパーを参照できます。
v-text / v-html
v-text は要素の textContent を、式の結果で置き換えます。{{ }} と違い要素の中身を丸ごと差し替えるため、初期表示のちらつきを避けたいときに向きます。
<span v-text="message"></span>
<!-- {{ message }} と同じ結果(HTML はエスケープされる) -->
v-html は innerHTML を設定し、生の HTML を描画します。
<div v-html="htmlContent"></div>
⚠️ セキュリティ:
v-htmlは XSS の温床になり得ます。信頼できる内容にのみ使い、ユーザー入力を絶対に渡さないでください。プレーンテキストにはv-textか{{ }}を使います。
v-bind(:)
属性・プロパティを式にバインドします。
<img :src="imageUrl" :alt="imageAlt">
<a :href="`/users/${user.id}`">プロフィール</a>
<button :disabled="isLoading">送信</button>
class のバインド
オブジェクト記法(キーが真のときクラスを付与)と配列記法が使えます。静的な class と併用すると両方がマージされます。
<!-- オブジェクト記法 -->
<div class="card" :class="{ active: isActive, 'is-done': todo.completed }"></div>
<!-- 配列記法 -->
<div :class="['badge', isError ? 'badge-error' : 'badge-ok']"></div>
style のバインド
<div :style="{ height: value * 2 + 'px', color: theme.ink }"></div>
v-if / v-else-if / v-else
条件付きでレンダリングします。条件が偽の要素は DOM に存在しません(生成・破棄される)。
<div v-if="count > 10">10 より大きい</div>
<div v-else-if="count > 5">5 より大きい</div>
<div v-else>5 以下</div>
v-else-if / v-else は、対応する v-if の直後の要素に置く必要があります。
v-show
v-show は要素を常に生成し、display の切り替えだけで表示・非表示を制御します。頻繁にトグルする要素は、生成・破棄を伴う v-if より v-show が軽量です。
<div v-show="isVisible">トグルできます</div>
v-for
配列・オブジェクト・数値・文字列を反復描画します。:key を付けると差分更新が安定します(推奨)。
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index }}: {{ item.name }}
</li>
</ul>
<template> でラッパーなし反復
ラッパー要素を増やさず複数ノードを繰り返したいときは、<template> に v-for を置きます。
<dl>
<template v-for="row in rows" :key="row.id">
<dt>{{ row.term }}</dt>
<dd>{{ row.description }}</dd>
</template>
</dl>
<template> には v-for または v-if のどちらか一方のみを置けます(同じ <template> に両方は不可)。両方必要なときは入れ子にします。
<template v-for="item in items" :key="item.id">
<div v-if="item.visible">{{ item.name }}</div>
</template>
v-for と v-if の併用
<template> 以外の通常要素では、同じ要素に v-for と v-if を併用できます。v-for が先に評価され、各反復で v-if が判定されます。
<li v-for="item in items" v-if="item.visible" :key="item.id">{{ item.name }}</li>
v-on(@)
イベントを購読します。ハンドラはメソッド名でも、インラインの式でも指定できます。
<button @click="handleClick">クリック</button>
<button @click="count++">インライン式も可</button>
<form @submit.prevent="handleSubmit">送信</form>
ハンドラのコンテキスト
すべてのイベントハンドラは、第 1 引数にイベント、第 2 引数に $ctx を受け取ります。
methods: {
handleClick(event, $ctx) {
// event … DOM イベント
// $ctx.element … DOM 要素
// $ctx.vnode … VNode インスタンス
// $ctx.userData … Proxy を介さないストレージ
}
}
イベント修飾子
| 修飾子 | 効果 |
|---|---|
.stop |
stopPropagation() |
.prevent |
preventDefault() |
.capture |
キャプチャフェーズで購読 |
.self |
イベントの発生源が自身のときだけ実行 |
.once |
一度だけ実行 |
<div @click.stop="onClick">伝播を止める</div>
<a @click.prevent="onNav">既定動作を抑止</a>
キー修飾子(KeyboardEvent)
.enter .tab .delete(Delete / Backspace).esc(.escape).space .up .down .left .right
<input @keyup.enter="submit" @keyup.esc="cancel">
マウスボタン修飾子(MouseEvent)
.left .middle .right
<a @mousedown.middle="openInNewTab">ホイールクリック</a>
<div @contextmenu.prevent="onMenu">右クリックメニューを抑止</div>
システム修飾キー
.shift .ctrl .alt .meta。.exact を加えると、ほかの修飾キーが押されていないことを要求します。
<button @click.shift="onShiftClick">Shift+クリック</button>
<button @click.ctrl.exact="onCtrlOnly">Ctrl だけのクリック</button>
v-model
フォーム要素との双方向バインドです。
<input v-model="message">
<textarea v-model="bio"></textarea>
修飾子
| 修飾子 | 効果 |
|---|---|
.lazy |
input ではなく change で同期 |
.number |
数値に変換 |
.trim |
前後の空白を除去 |
<input v-model.trim="username">
<input v-model.number="age" type="number">
要素ごとの挙動
<!-- チェックボックス(真偽値) -->
<input type="checkbox" v-model="isChecked">
<!-- 真偽値のカスタム化 -->
<input type="checkbox" v-model="status" :true-value="'yes'" :false-value="'no'">
<!-- 配列にバインド(複数選択) -->
<input type="checkbox" value="a" v-model="selected">
<input type="checkbox" value="b" v-model="selected">
<!-- ラジオ -->
<input type="radio" value="a" v-model="picked">
<input type="radio" value="b" v-model="picked">
<!-- セレクト(v-for で動的生成しても選択が再適用される) -->
<select v-model="choice">
<option v-for="o in options" :key="o.value" :value="o.value">{{ o.label }}</option>
</select>
- チェックボックス — 真偽値、
:true-value/:false-valueのペア、または配列(valueを出し入れ)にバインド - ラジオ — 選択された
value(:value)にバインド - セレクト — 選択中の
<option>の値にバインド
書き込み可能な computed({ get, set })を v-model の対象にすることもできます(「基本ガイド」参照)。
v-focus
フォーカス制御を宣言的に行います。フォーカスは requestAnimationFrame で遅延されるため、v-if 直後に現れた要素にも確実に当たります。
<!-- マウント後に一度フォーカス -->
<input v-focus>
<!-- フォーカス+全選択 -->
<input v-focus.select>
<!-- フォーカス+キャレットを末尾へ -->
<input v-focus.cursor-end value="prefilled">
<!-- 条件付き(偽→真に変わった瞬間に発火) -->
<input v-focus="isEditing">
- 式なし — マウント後に一度だけフォーカス
- 式あり — 偽 → 真の変化のときだけフォーカス(毎回の更新で再フォーカスしない)
- 修飾子
.select(全選択)/.cursor-end(末尾にキャレット)
ライフサイクルフック
要素のライフサイクルの各段階でコードを実行できます。@(v-on)でフックを購読し、各フックは $ctx(element / vnode / userData)を受け取ります。外部ライブラリ連携やアニメーションの起点に使います。
<div v-if="show"
@mount="onMount"
@mounted="onMounted"
@update="onUpdate"
@updated="onUpdated"
@unmount="onUnmount"
@unmounted="onUnmounted">
内容
</div>
| フック | タイミング |
|---|---|
@mount |
DOM へのマウント直前 |
@mounted |
マウント直後 |
@update |
更新直前 |
@updated |
更新直後 |
@unmount |
破棄開始の直前(要素はまだ DOM 上) |
@unmounted |
破棄完了後($ctx.element で参照は可能) |
userData と自動クリーンアップ
$ctx.userData は、リアクティブプロキシの影響を受けない Map ストレージです。Chart.js などサードパーティのインスタンス保持に最適で、close() を持つオブジェクトは破棄時に自動でクリーンアップされます。
methods: {
onMounted($ctx) {
const chart = new Chart($ctx.element.querySelector('canvas'), { /* ... */ });
$ctx.userData.set('chart', chart);
},
onUpdated($ctx) {
$ctx.userData.get('chart')?.update();
},
onUnmount($ctx) {
$ctx.userData.get('chart')?.destroy();
}
}
破棄フェーズの順序は次のとおりです。
@unmount発火(要素はまだ DOM 上)userDataの自動クリーンアップ(close()呼び出し)- 子ノードを再帰的に破棄
- 依存の登録解除
- ディレクティブマネージャの後始末
@unmounted発火(DOM からは外れているが$ctx.elementは参照可能)
v-if での表示切替や v-for の各要素でもフックは発火します。動く例は「ライフサイクルフック」を参照してください。
Observer ディレクティブ
ブラウザ標準の Observer API を、ディレクティブとして宣言的に扱えます。いずれも破棄時に自動で disconnect され、ハンドラは $ctx を受け取ります。オプションは :options.<種別> または汎用の :options で渡します。
v-resize
ResizeObserver で要素サイズの変化を監視します。
<div v-resize="onResize" :options.resize="{ box: 'border-box' }">
{{ width }}px × {{ height }}px
</div>
methods: {
onResize(entries, $ctx) {
const r = entries[0].contentRect;
this.width = Math.round(r.width);
this.height = Math.round(r.height);
}
}
→ 動く例: Resize Observer
v-intersection
IntersectionObserver で要素の可視状態を検知します。遅延読み込み・無限スクロール・スクロール演出に。
<div v-intersection="onIntersection" :options.intersection="{ threshold: 0.5 }">
{{ isVisible ? '見えている' : '見えていない' }}
</div>
methods: {
onIntersection(entries, $ctx) {
this.isVisible = entries[0].isIntersecting;
}
}
→ 動く例: Intersection Observer
v-performance
PerformanceObserver でパフォーマンスエントリを観測します。ハンドラの引数は (entries, observer, options, $ctx) です。
<div v-performance="onPerformance" :options.performance="{ entryTypes: ['measure', 'mark'] }">
計測対象
</div>
methods: {
onPerformance(entries, observer, options, $ctx) {
entries.getEntries().forEach(e => console.log(`${e.name}: ${e.duration}ms`));
}
}
→ 動く例: Performance Observer