Vue.js の computed が更新されない
computed はリアクティブな依存関係だけを追跡してキャッシュするため、リアクティブでない値を参照していると再計算されない。
依存する値をすべて ref / reactive にするのが解決の軸。
getter 内の副作用も誤動作のもとになる。
公開: 更新:
要約
Vue.js の computed が更新されないとき、ほとんどの場合は「getter が参照している値がリアクティブでない」ことが原因です。
computed はリアクティブな依存関係にもとづいてキャッシュされ、依存が変化したときだけ再計算されます(算出プロパティの公式ガイド(新しいタブで開く))。
リアクティブでない値はそもそも依存として追跡されないため、いくら変更しても computed は古いキャッシュを返し続けます。
実行例
node:20 で計算プロパティだけを取り出して動かすと、リアクティブでない変数を書き換えても computed は初回の値を返したままで、Date.now() を返す getter も二度目に同じ値を返す。
依存を ref で宣言し直すと同じ書き換えが再計算に反映され、toRefs() を通した分割代入でも追跡が続く。
$ node bad.mjs
初回 : 2
plain=5 の後 : 2
Date.now() の computed が同じ値を返すか: true$ node solve_ref.mjs
初回 : 2
plain=5 の後 : 10$ node solve_torefs.mjs
初回 : 3
push 後: 4— 2026-09-09 時点の出力
検証環境
- 検証日
- 実行環境
node:20Debian GNU/Linux 12 (bookworm)- バージョン
- Node.js 20.20.2
- npm 10.8.2
- Python 3.11.2
- Git 2.39.5
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
よくある原因
- リアクティブでない値への依存:
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・メソッド側で行います。
これは公式ガイドでもベストプラクティスとして明記されています。