Vue.js の computed が更新されない
computed はリアクティブな依存関係だけを追跡してキャッシュするため、リアクティブでない値を参照していると再計算されない。
依存する値をすべて ref / reactive にするのが解決の軸。
getter 内の副作用も誤動作のもとになる。
#vue#computed#reactivity#composition-api#ref
公開: 更新:
要約
Vue.js の computed が更新されないとき、ほとんどの場合は「getter が参照している値がリアクティブでない」ことが原因です。
computed はリアクティブな依存関係にもとづいてキャッシュされ、依存が変化したときだけ再計算されます(算出プロパティの公式ガイド)。
リアクティブでない値はそもそも依存として追跡されないため、いくら変更しても computed は古いキャッシュを返し続けます。
よくある原因
- リアクティブでない値への依存:
let count = 0のような通常の変数を getter で参照しても、Vue は変更を検知できない。 - 分割代入によるリアクティビティ切れ:
const { items } = reactive(state)のように分割代入した時点で、itemsはただの値になり追跡から外れる。 - 追跡対象が無い:
computed(() => Date.now())は依存するリアクティブな値が無いため、初回以降は一度も再計算されない。 - getter 内の副作用: getter の中で別の状態を書き換えたり非同期処理を待ったりすると、算出の結果と画面の状態がズレて「更新されない」ように見える。
解決策
1. 依存を ref / reactive にする
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
count.value++ // doubled が 2 に更新されるgetter から読む値はすべて ref() か reactive() で宣言します。
これが computed を更新するための前提条件です。
2. 分割代入には toRefs を使う
import { reactive, toRefs, computed } from 'vue'
const state = reactive({ items: [] as string[] })
const { items } = toRefs(state)
const total = computed(() => items.value.length)toRefs() を経由すれば、分割代入してもリアクティブな参照(ref)として扱えます。
3. 自発的に変わる値は computed にしない
現在時刻のような値は依存追跡で更新できないため、ref を setInterval で更新するか、イベントに応じて更新する設計にします。
4. 副作用を getter から追い出す
getter は「他の状態から値を算出して返す」純粋な関数に保ち、状態の書き換えや API 呼び出しは watch・メソッド側で行います。
これは公式ガイドでもベストプラクティスとして明記されています。