Computed Properties - 派生値とキャッシュ
ロードマップ: Vue.js学習ロードマップ
What(何についてか)
computed properties は、リアクティブな元データから派生値を宣言的に作る仕組みである。テンプレート式の複雑化を避けつつ、表示や判定に必要な値へ名前を与えられる。
Composition API では computed() を使い、getter 関数の返り値を computed ref として扱う。
Why(なぜ必要か)
テンプレート内に条件分岐や計算式を直接書くと、可読性が下がり、同じロジックを複数箇所で再利用しにくくなる。computed を使うことで、以下の利点が得られる。
- テンプレートから計算ロジックを分離できる
- 派生値に意味のある名前を与えられる
- 依存関係に基づいて再計算されるため、不要な実行を避けられる
特に、複数の reactive state から導かれる表示用の値を扱う時に有効である。
How(どう動くか)
computed() は getter 関数の中で参照された reactive state を依存関係として追跡する。依存関係に変化があった時のみ再計算し、それ以外では前回の計算結果を再利用する。
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>computed ref は template 内では自動でアンラップされるため、.value を書かずに参照できる。
flowchart TD A["source state\nauthor.books"] --> B["computed getter\npublishedBooksMessage"] B --> C["derived value\nYes / No"] C --> D["template binding"]
Computed と Methods の違い
computed と通常の関数は、見た目上は同じ結果を返せる場合がある。ただし、再実行の条件が異なる。
- computed: 依存する reactive state が変化した時だけ再計算する
- method / 関数: 再レンダリングのたびに毎回実行される
このため、重い計算や複数箇所から参照される派生値では computed が有利である。一方、キャッシュが不要な処理やその場で毎回実行したい処理は通常の関数で十分である。
Writable Computed
computed は通常 getter-only だが、getter / setter の両方を持たせることで writable computed を作れる。
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
get() {
return firstName.value + ' ' + lastName.value
},
set(newValue) {
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>これは、UI 上は 1 つの値として扱いたいが、内部では複数の state に分けて保持したい場合に使える。ただし常用すべき機能ではなく、必要になった時だけ採用する。
Previous Value の参照
Vue 3.4 以降では、computed getter が前回の返り値を受け取れる。これにより、条件に応じて直前の有効値を保持するような挙動を作れる。
<script setup>
import { ref, computed } from 'vue'
const count = ref(2)
const alwaysSmall = computed((previous) => {
if (count.value <= 3) {
return count.value
}
return previous
})
</script>この機能は特殊要件向けであり、日常的な computed の利用では必須ではない。
Best Practices
getter は副作用を持たせない
computed getter の責務は純粋計算に限定する。以下は避ける。
- 他の state の書き換え
- 非同期通信
- DOM 操作
副作用が必要な場合は watch() やイベントハンドラに分離する。
computed の返り値は直接変更しない
computed が返す値は元 state から導かれた派生結果であり、スナップショットとして扱う。変更したい場合は computed そのものではなく、依存元の state を更新する。
ref と reactive の補足
computed の依存元は ref() でも reactive() でもよい。今回の公式サンプルではオブジェクト全体を自然に扱うため reactive() が使われているが、computed の本質は変わらない。
Vue 3 学習初期では、状態管理の一貫性を保ちやすい ref() を基本にしつつ、オブジェクト全体をまとまりとして扱う文脈では reactive() も選択肢になる。
Options API → Composition API 差分(補足)
| 項目 | Options API(旧) | Composition API(新) |
|---|---|---|
| computed の定義場所 | computed: { ... } | const x = computed(() => ...) |
| state 参照 | this.author.books | author.books または author.value.books |
| 依存関係の見え方 | this 経由で暗黙的 | getter 内の参照で明示的 |
| writable computed | get() / set() を computed オプションで定義 | computed({ get, set }) |
Key Concepts
| 用語 | 説明 |
|---|---|
| computed | reactive state から導かれる派生値を表す仕組み |
| computed ref | computed() が返す、キャッシュ付きのリアクティブ値 |
| getter | 派生値を計算して返す関数 |
| setter | writable computed で代入時の挙動を定義する関数 |
| reactive dependency | computed が再計算される条件となる依存元の reactive state |
| caching | 依存関係が変わらない限り前回結果を再利用する仕組み |