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.booksauthor.books または author.value.books
依存関係の見え方this 経由で暗黙的getter 内の参照で明示的
writable computedget() / set()computed オプションで定義computed({ get, set })

Key Concepts

用語説明
computedreactive state から導かれる派生値を表す仕組み
computed refcomputed() が返す、キャッシュ付きのリアクティブ値
getter派生値を計算して返す関数
setterwritable computed で代入時の挙動を定義する関数
reactive dependencycomputed が再計算される条件となる依存元の reactive state
caching依存関係が変わらない限り前回結果を再利用する仕組み