できない.dev

Vue で watch が発火しない

watch が反応しないのは、監視対象に値を直接渡しているか、ネストの変更を浅い監視で見逃しているのが主因。
ref はそのまま、reactive のプロパティはゲッター関数で渡し、深い変更は deep オプションを付ける。

公開: 更新:

実行例あり(2026-09-08 に実環境で検証)

要約

watch のコールバックが呼ばれないときは、ほとんどが「監視対象の渡し方」の問題です。
Vue の watch は ref・ゲッター関数・reactive オブジェクトを監視対象に取れますが、reactive のプロパティを値のまま渡すと追跡できません。
ref はそのまま、reactive のプロパティはゲッター関数で渡すのが基本です。

実行例

state.count をそのまま渡した実行では Invalid watch source の警告が出て、値が 1 に変わっても watch は動かなかった。
同じ値をゲッターで包むと 0 -> 1 の変化を受け取れており、ネストしたオブジェクトの書き換えも deep を付けた実行でだけコールバックが走っている。

$ node bad.mjs
[Vue warn]: Invalid watch source:  0 A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.
state.count = 1 (watch は発火していない)
$ node solve_getter.mjs
ゲッターの watch が発火: 0 -> 1
$ node solve_deep.mjs
deep ありの watch が発火
user.value.profile.name = bar

— 2026-09-08 時点の出力

検証環境

検証日
実行環境
node:20Debian GNU/Linux 12 (bookworm)
バージョン
  • Node.js 20.20.2
  • npm 10.8.2
  • Python 3.11.2
  • Git 2.39.5

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. 値で渡している: watch(state.count, cb) は state.count の評価結果(数値)を渡しているだけで、依存追跡が効かない。
  2. 浅い監視: reactive オブジェクトを監視しても、ref に入れたオブジェクトをゲッターで返すような場合はトップレベルの変更しか追わず、ネストの変更は検知されない。
  3. スナップショット: コンポーザブルなどから受け取った値をコピーして監視している。
  4. watchEffect の依存漏れ: コールバック内で実際にアクセスした値だけが依存になる。
    条件分岐で参照しなかった値は追跡されない。

解決策

1. ref はそのまま、reactive はゲッターで渡す

import { ref, reactive, watch } from 'vue'
 
const count = ref(0)
watch(count, (n) => console.log(n)) // ref はそのまま
 
const state = reactive({ count: 0 })
watch(() => state.count, (n) => console.log(n)) // ゲッターで渡す

ウォッチャの公式ガイド(新しいタブで開く) のとおり、watch(state.count, ...) のように値を直接渡すと監視は機能しません。

2. 深い変更は deep オプション

const obj = reactive({ nested: { value: 1 } })
 
watch(
  () => obj.nested,
  (n) => console.log(n),
  { deep: true },
)

reactive オブジェクトを直接渡した場合は自動的に deep になりますが、ゲッターでネストしたオブジェクトを返す場合は deep: true を明示します。

3. 初回実行は immediate

watch(
  source,
  (v) => console.log(v),
  { immediate: true }, // 監視開始時にも一度実行
)

4. watchEffect は参照した依存だけ追う

import { watchEffect } from 'vue'
 
watchEffect(() => {
  // ここでアクセスした reactive な値が自動的に依存になる
  console.log(state.count)
})

条件によって参照されない値は依存に含まれない点に注意します。
特定の値だけを監視したいなら watch を使う方が明示的です。

この記事は役立ちましたか?