はじめに
Vue.js における methods、computed、watch はすべて「状態(データ)をどのように扱うか・反応するか」を定義するための仕組みです。
methodsとの違いの明確化
まずは前回の内容である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の特徴
- 変更監視:特定のデータの変化を監視
- 副作用の実行:データ変更時に非同期処理や複雑なロジックを実行
- 新旧値の取得:変更前後の値にアクセス可能
| オプション | 主な役割 | タイミング | 特徴 |
|---|---|---|---|
methods | 関数として呼び出す処理 | 明示的に呼び出されたとき | データを変更したり処理したりするための通常の関数 |
computed | 計算結果を自動で返す | 依存するデータが変化したときに自動再評価 | キャッシュ付きのリアクティブ計算プロパティ。表示用などに最適 |
watch | 値の変化を監視して処理を実行 | 監視対象のデータが変わった瞬間に実行される副作用 | API呼び出しやログ出力など、処理をトリガーさせたいときに使う |
computedの詳細解説
computedプロパティは依存するデータに基づいて計算された値を返します。リアクティブな依存関係を自動的に追跡し、依存するデータが変更された時のみ再計算します。
基本的な使用例
export default {
data() {
return {
firstName: '太郎',
lastName: '山田'
}
},
computed: {
fullName() {
return `${this.lastName} ${this.firstName}`;
}
}
}
<template>
<p>氏名: {{ fullName }}</p>
</template>
セッター付きcomputedプロパティ
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の詳細解説
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
}
}
}
computed vs watch の使い分け
computedを使うべきケース
- 派生データを表示するため
- 複数のデータに依存する計算が必要な場合
- キャッシュによるパフォーマンス向上が必要な場合
export default {
data() {
return {
width: 0,
height: 0
}
},
computed: {
area() {
return this.width * this.height;
}
}
}
watchを使うべきケース
- データ変更時に非同期処理を実行する場合
- データ変更時に副作用を実行する必要がある場合
- 変更前後の値に基づいて処理を行う場合
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();
}
}
}
パフォーマンス比較
computedの利点
- 依存関係が変わらない限り再計算されない
- 複数の依存関係を効率的に管理
- テンプレートがシンプルになる
watchの利点
- 変更時の具体的な動作を定義可能
- 非同期処理を扱える
- 変更前後の値にアクセス可能
実践的なユースケース
フォームバリデーション(computed使用)
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;
}
}
}
APIポーリング(watch使用)
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);
}
}
}
watchの過剰使用
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はどちらもリアクティブなシステムの重要な部分ですが、適切な使い分けが重要です。
- computed:派生データの計算、テンプレート内での複雑な式の簡素化、複数の依存関係を持つ値の効率的な管理に使用
- watch:データ変更時の副作用の実行、非同期処理、変更前後の値に基づく処理、パフォーマンスが重要な操作(デバウンスなど)に使用
methodsとの主な違いは、methodsが明示的に呼び出される関数であるのに対し、computedは依存関係に基づいて自動的に計算されるプロパティ、watchはデータ変更を監視するためのフックである点です。
適切に使い分けることで、より効率的でメンテナンス性の高いVue.jsアプリケーションを構築できます。