
Vue.js ルーティング演習問題
基本概念まとめ 1. ルーティングの基本設定 2. ナビゲーションガード 3. 動的ルーティング 演習問題(全24問) 初級問題(6問) 基本ルーティング ナビ […]
Vue.jsのディレクティブは、HTML要素に特別な振る舞いを追加するための命令です。この記事では、Vue.jsで最もよく使用される4つのディレクティブ(v-bind, v-model, v-for, v-if)について、詳細な解説と実践的なデモを通じて学びます。
v-bind
は、HTML属性をVueインスタンスのデータにバインドするために使用します。省略記法として:
が使用できます。
<!-- 完全な記法 -->
<a v-bind:href="url">リンク</a>
<!-- 省略記法 -->
<a :href="url">リンク</a>
<div id="app">
<img :src="imageSrc" :alt="imageAlt" :width="imageWidth">
<button :disabled="isButtonDisabled">送信</button>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
imageSrc: 'https://vuejs.org/images/logo.png',
imageAlt: 'Vue.jsロゴ',
imageWidth: 100,
isButtonDisabled: true
}
}
}).mount('#app');
</script>
v-bind
はクラスやスタイルのバインディングにも特に便利です。
<div id="app">
<div
:class="{ active: isActive, 'text-danger': hasError }"
:style="{ color: textColor, fontSize: fontSize + 'px' }">
動的なクラスとスタイル
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
isActive: true,
hasError: false,
textColor: 'blue',
fontSize: 20
}
}
}).mount('#app');
</script>
v-model
は、フォーム入力とアプリケーションデータの双方向バインディングを実現します。
<input v-model="message" placeholder="編集してみてください">
<p>入力されたメッセージ: {{ message }}</p>
<div id="app">
<h2>フォーム入力デモ</h2>
<div>
<label>テキスト:</label>
<input v-model="textValue" type="text">
</div>
<div>
<label>チェックボックス:</label>
<input v-model="checked" type="checkbox">
{{ checked ? 'ON' : 'OFF' }}
</div>
<div>
<label>複数チェックボックス:</label>
<div v-for="option in options" :key="option.id">
<input
type="checkbox"
v-model="selectedOptions"
:value="option.id"
:id="'option-' + option.id">
<label :for="'option-' + option.id">{{ option.label }}</label>
</div>
選択済み: {{ selectedOptions }}
</div>
<div>
<label>ラジオボタン:</label>
<div v-for="item in radioItems" :key="item.value">
<input
type="radio"
v-model="radioValue"
:value="item.value"
:id="'radio-' + item.value">
<label :for="'radio-' + item.value">{{ item.label }}</label>
</div>
選択済み: {{ radioValue }}
</div>
<div>
<label>セレクトボックス:</label>
<select v-model="selectedItem">
<option disabled value="">選択してください</option>
<option v-for="item in selectItems" :key="item.value" :value="item.value">
{{ item.label }}
</option>
</select>
選択済み: {{ selectedItem }}
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
textValue: '',
checked: false,
options: [
{ id: 1, label: 'オプション1' },
{ id: 2, label: 'オプション2' },
{ id: 3, label: 'オプション3' }
],
selectedOptions: [],
radioItems: [
{ value: 'A', label: '選択肢A' },
{ value: 'B', label: '選択肢B' },
{ value: 'C', label: '選択肢C' }
],
radioValue: 'A',
selectItems: [
{ value: 'apple', label: 'りんご' },
{ value: 'banana', label: 'バナナ' },
{ value: 'orange', label: 'オレンジ' }
],
selectedItem: ''
}
}
}).mount('#app');
</script>
v-for
は、配列やオブジェクトのアイテムを基に要素を繰り返しレンダリングします。
<!-- 配列のレンダリング -->
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index + 1 }}. {{ item.text }}
</li>
</ul>
<!-- オブジェクトのレンダリング -->
<ul>
<li v-for="(value, key, index) in object">
{{ index }}. {{ key }}: {{ value }}
</li>
</ul>
<div id="app">
<h2>商品一覧</h2>
<div class="product-filter">
<input v-model="filterText" placeholder="商品名でフィルタリング">
<button @click="sortByPrice">価格でソート</button>
</div>
<ul class="product-list">
<li v-for="product in filteredProducts" :key="product.id" class="product-item">
<h3>{{ product.name }}</h3>
<p>価格: {{ product.price | currency }}</p>
<p>在庫: {{ product.stock }}個</p>
<button @click="addToCart(product)">カートに追加</button>
</li>
</ul>
<h3>カート ({{ cart.length }}点)</h3>
<ul>
<li v-for="(item, index) in cart" :key="index">
{{ item.name }} - {{ item.price | currency }}
<button @click="removeFromCart(index)">削除</button>
</li>
</ul>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
filterText: '',
products: [
{ id: 1, name: 'ノートパソコン', price: 120000, stock: 10 },
{ id: 2, name: 'スマートフォン', price: 80000, stock: 15 },
{ id: 3, name: 'タブレット', price: 50000, stock: 8 },
{ id: 4, name: 'ワイヤレスイヤホン', price: 15000, stock: 20 },
{ id: 5, name: 'スマートウォッチ', price: 25000, stock: 12 }
],
cart: [],
sortAscending: true
}
},
computed: {
filteredProducts() {
return this.products
.filter(product =>
product.name.toLowerCase().includes(this.filterText.toLowerCase())
)
.sort((a, b) => this.sortAscending ? a.price - b.price : b.price - a.price);
}
},
methods: {
addToCart(product) {
if (product.stock > 0) {
this.cart.push({...product});
product.stock--;
}
},
removeFromCart(index) {
const item = this.cart[index];
const product = this.products.find(p => p.id === item.id);
if (product) {
product.stock++;
}
this.cart.splice(index, 1);
},
sortByPrice() {
this.sortAscending = !this.sortAscending;
}
},
filters: {
currency(value) {
return '¥' + value.toLocaleString();
}
}
}).mount('#app');
</script>
<style>
.product-list {
list-style: none;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
.product-item {
border: 1px solid #ddd;
padding: 15px;
border-radius: 5px;
}
.product-filter {
margin-bottom: 20px;
}
</style>
v-if
は条件に基づいて要素の表示/非表示を制御します。v-else-if
やv-else
と組み合わせて使用できます。
<div v-if="type === 'A'">Type A</div>
<div v-else-if="type === 'B'">Type B</div>
<div v-else>Type C</div>
v-if
とv-show
の違いを理解することが重要です。
特徴 | v-if | v-show |
---|---|---|
DOMへの追加/削除 | 条件がfalseならDOMから削除 | display: noneで非表示にする |
初期レンダリング | 条件がfalseならレンダリングしない | 常にレンダリングされる |
切り替えコスト | 高い(DOM操作が必要) | 低い(スタイル変更のみ) |
使用例 | 初期表示が不要な大きなコンポーネント | 頻繁に切り替わる要素 |
<div id="app">
<h2>ユーザープロファイル</h2>
<div v-if="isLoading" class="loading">
読み込み中...
</div>
<div v-else-if="error" class="error">
エラーが発生しました: {{ error }}
</div>
<div v-else>
<div class="profile">
<img :src="user.avatar" alt="プロフィール画像" class="avatar">
<h3>{{ user.name }}</h3>
<p>{{ user.bio }}</p>
<div v-if="user.isAdmin" class="admin-badge">
管理者
</div>
<div v-show="user.hasPosts">
<h4>最近の投稿</h4>
<ul>
<li v-for="post in user.posts" :key="post.id">
{{ post.title }}
</li>
</ul>
</div>
<div v-show="!user.hasPosts">
投稿がありません
</div>
</div>
</div>
<button @click="fetchUser">再読み込み</button>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
isLoading: false,
error: null,
user: null
}
},
created() {
this.fetchUser();
},
methods: {
fetchUser() {
this.isLoading = true;
this.error = null;
// 模擬API呼び出し
setTimeout(() => {
try {
// ランダムに成功または失敗
if (Math.random() > 0.2) {
this.user = {
name: '山田太郎',
avatar: 'https://i.pravatar.cc/150?img=3',
bio: 'フロントエンド開発者。Vue.jsが大好きです。',
isAdmin: true,
hasPosts: true,
posts: [
{ id: 1, title: 'Vue.js入門' },
{ id: 2, title: 'Vuexの使い方' },
{ id: 3, title: 'Vue Router徹底解説' }
]
};
} else {
throw new Error('ユーザーデータの取得に失敗しました');
}
} catch (err) {
this.error = err.message;
} finally {
this.isLoading = false;
}
}, 1000);
}
}
}).mount('#app');
</script>
<style>
.profile {
max-width: 500px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
display: block;
margin: 0 auto 15px;
}
.loading, .error {
padding: 20px;
text-align: center;
background-color: #f5f5f5;
border-radius: 8px;
}
.error {
background-color: #ffebee;
color: #c62828;
}
.admin-badge {
display: inline-block;
padding: 3px 8px;
background-color: #ff5722;
color: white;
border-radius: 4px;
font-size: 0.8em;
margin-bottom: 15px;
}
</style>
この記事では、Vue.jsの主要な4つのディレクティブについて詳しく解説しました:
これらのディレクティブを組み合わせることで、動的でインタラクティブなWebアプリケーションを効率的に構築できます。実際のプロジェクトでは、これらの基本を理解した上で、より複雑なコンポーネントやアプリケーション構造へと進んでいくことになります。