
Vue.js ルーティング演習問題
基本概念まとめ 1. ルーティングの基本設定 2. ナビゲーションガード 3. 動的ルーティング 演習問題(全24問) 初級問題(6問) 基本ルーティング ナビ […]
Vue.js における methods
、computed
、watch
はすべて「状態(データ)をどのように扱うか・反応するか」を定義するための仕組みです。
まずは前回の内容であるmethods
との違いを明確にしましょう。
export default {
data() {
return {
price: 100,
quantity: 2
}
},
methods: {
calculateTotal() {
return this.price * this.quantity;
}
},
computed: {
total() {
return this.price * this.quantity;
}
},
watch: {
price(newVal, oldVal) {
console.log(`価格が${oldVal}から${newVal}に変更されました`);
}
}
}
オプション | 主な役割 | タイミング | 特徴 |
---|---|---|---|
methods | 関数として呼び出す処理 | 明示的に呼び出されたとき | データを変更したり処理したりするための通常の関数 |
computed | 計算結果を自動で返す | 依存するデータが変化したときに自動再評価 | キャッシュ付きのリアクティブ計算プロパティ。表示用などに最適 |
watch | 値の変化を監視して処理を実行 | 監視対象のデータが変わった瞬間に実行される副作用 | API呼び出しやログ出力など、処理をトリガーさせたいときに使う |
computed
プロパティは依存するデータに基づいて計算された値を返します。リアクティブな依存関係を自動的に追跡し、依存するデータが変更された時のみ再計算します。
export default {
data() {
return {
firstName: '太郎',
lastName: '山田'
}
},
computed: {
fullName() {
return `${this.lastName} ${this.firstName}`;
}
}
}
<template>
<p>氏名: {{ fullName }}</p>
</template>
computedプロパティはゲッターだけでなくセッターも定義できます。
export default {
data() {
return {
firstName: '太郎',
lastName: '山田'
}
},
computed: {
fullName: {
get() {
return `${this.lastName} ${this.firstName}`;
},
set(newValue) {
const names = newValue.split(' ');
this.lastName = names[0];
this.firstName = names[1] || '';
}
}
}
}
export default {
data() {
return {
items: [
{ id: 1, name: '商品A', price: 1000, stock: 5 },
{ id: 2, name: '商品B', price: 2000, stock: 3 },
{ id: 3, name: '商品C', price: 1500, stock: 0 }
],
discountRate: 0.1
}
},
computed: {
availableItems() {
return this.items.filter(item => item.stock > 0);
},
totalPrice() {
return this.availableItems.reduce((sum, item) => sum + item.price, 0);
},
discountedPrice() {
return this.totalPrice * (1 - this.discountRate);
}
}
}
watch
は特定のデータソースを監視し、変更があった時にコールバック関数を実行します。非同期処理や複雑なロジックを実行するのに適しています。
export default {
data() {
return {
question: '',
answer: '質問を入力してください...'
}
},
watch: {
question(newQuestion, oldQuestion) {
if (newQuestion.includes('?')) {
this.getAnswer();
}
}
},
methods: {
async getAnswer() {
this.answer = '考え中...';
try {
const res = await fetch('https://yesno.wtf/api');
const data = await res.json();
this.answer = data.answer;
} catch (error) {
this.answer = 'エラーが発生しました: ' + error;
}
}
}
}
オブジェクトや配列のネストされたプロパティを監視するにはdeep
オプションを使用します。
export default {
data() {
return {
user: {
name: '太郎',
preferences: {
theme: 'light',
notifications: true
}
}
}
},
watch: {
user: {
handler(newVal, oldVal) {
console.log('ユーザー情報が変更されました');
},
deep: true
}
}
}
immediate
オプションで作成時にすぐにハンドラを実行できます。
export default {
data() {
return {
apiKey: localStorage.getItem('apiKey') || ''
}
},
watch: {
apiKey: {
handler(newVal) {
localStorage.setItem('apiKey', newVal);
},
immediate: true
}
}
}
export default {
data() {
return {
width: 0,
height: 0
}
},
computed: {
area() {
return this.width * this.height;
}
}
}
export default {
data() {
return {
searchQuery: '',
searchResults: []
}
},
watch: {
searchQuery(newVal) {
if (newVal.length > 2) {
this.debouncedSearch();
}
}
},
created() {
this.debouncedSearch = _.debounce(this.doSearch, 500);
},
methods: {
async doSearch() {
const response = await fetch(`/api/search?q=${this.searchQuery}`);
this.searchResults = await response.json();
}
}
}
export default {
data() {
return {
email: '',
password: '',
passwordConfirmation: ''
}
},
computed: {
emailError() {
if (!this.email) return 'メールアドレスは必須です';
if (!this.email.includes('@')) return '有効なメールアドレスを入力してください';
return '';
},
passwordError() {
if (!this.password) return 'パスワードは必須です';
if (this.password.length < 8) return 'パスワードは8文字以上必要です';
return '';
},
passwordMatchError() {
if (this.password !== this.passwordConfirmation) {
return 'パスワードが一致しません';
}
return '';
},
isValid() {
return !this.emailError && !this.passwordError && !this.passwordMatchError;
}
}
}
export default {
data() {
return {
status: 'idle',
pollingInterval: null,
lastUpdated: null
}
},
watch: {
status(newVal) {
if (newVal === 'active' && !this.pollingInterval) {
this.startPolling();
} else if (newVal !== 'active' && this.pollingInterval) {
this.stopPolling();
}
}
},
methods: {
startPolling() {
this.pollingInterval = setInterval(() => {
this.fetchStatus();
}, 5000);
this.fetchStatus();
},
stopPolling() {
clearInterval(this.pollingInterval);
this.pollingInterval = null;
},
async fetchStatus() {
const response = await fetch('/api/status');
const data = await response.json();
this.lastUpdated = data.timestamp;
}
},
beforeDestroy() {
this.stopPolling();
}
}
export default {
data() {
return {
items: [],
filteredItems: [] // ← データとcomputedの混合は避ける
}
},
watch: {
items() {
this.filteredItems = this.items.filter(item => item.active);
// computedプロパティを使用する方が適切
}
}
}
export default {
data() {
return {
items: []
}
},
computed: {
filteredItems() {
return this.items.filter(item => item.active);
}
}
}
export default {
data() {
return {
width: 0,
height: 0,
area: 0 // ← computedで計算すべき
}
},
watch: {
width() {
this.area = this.width * this.height;
},
height() {
this.area = this.width * this.height;
}
}
}
methods
、computed
、watch
の3つの主要なオプションの違いを表で示します。(再掲)
オプション | 主な役割 | タイミング | 特徴 |
---|---|---|---|
methods | 関数として呼び出す処理 | 明示的に呼び出されたとき | データを変更したり処理したりするための通常の関数 |
computed | 計算結果を自動で返す | 依存するデータが変化したときに自動再評価 | キャッシュ付きのリアクティブ計算プロパティ。表示用などに最適 |
watch | 値の変化を監視して処理を実行 | 監視対象のデータが変わった瞬間に実行される副作用 | API呼び出しやログ出力など、処理をトリガーさせたいときに使う |
Vue.jsのcomputed
とwatch
はどちらもリアクティブなシステムの重要な部分ですが、適切な使い分けが重要です。
methods
との主な違いは、methods
が明示的に呼び出される関数であるのに対し、computed
は依存関係に基づいて自動的に計算されるプロパティ、watch
はデータ変更を監視するためのフックである点です。
適切に使い分けることで、より効率的でメンテナンス性の高いVue.jsアプリケーションを構築できます。