Vue.js Reactivity Fundamentals - リアクティビティの基礎
ロードマップ: Vue.js学習ロードマップ
What(何についてか)
Vueのリアクティビティシステム。状態の変更を自動検知してDOMを更新する仕組み。
Why(なぜ必要か)
Vueの根幹。状態を変えれば画面が勝手に追従するのは、このシステムがあるから。
How(どう動くか)
ref() — 状態宣言の基本
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">{{ count }}</button>
</template>- JS内:
count.valueでアクセス(.value必須) - テンプレート内:
{{ count }}でOK(.value不要、自動アンラップ)
Why Refs? — .value の仕組み
graph LR A["ref(0)"] -->|".value 読み取り"| B["getter: track()"] A -->|".value 書き込み"| C["setter: trigger()"] B --> D["誰がこの値を使ってるか記録"] C --> E["値が変わったら再描画を通知"]
JSには「変数が変更された」を検知する仕組みがない。.value はgetter/setterのフックで、Vueが変更を検知するための窓口。
<script setup> は setup() 関数のシンタックスシュガー。コンパイル時に自動変換される。トップレベルの変数・関数は自動でテンプレートに公開。
Deep Reactivity
ネストされたオブジェクトや配列の変更も全て検知される。
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
obj.value.nested.count++ // ✅ 検知される
obj.value.arr.push('baz') // ✅ 検知されるDOM更新タイミング(nextTick)
状態変更後、DOM更新は即座ではない。Vueは複数変更をバッファリングして次ティックでまとめて更新する。
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// ここでDOMが更新済み
}reactive() — もう一つの宣言方法
const state = reactive({ count: 0 })
state.count++ // .value 不要制限が多い:
- プリミティブ型不可(string, number, boolean)
- オブジェクト全体の置き換え不可
- 分割代入でリアクティブが壊れる
結論: ref() を使う。
refのアンラップルール
アンラップ = refの包み(.value)を自動で剥がして中身を取り出すこと。
| 状況 | .value 必要? |
|---|---|
<script setup> 内のJS | 必要 |
| テンプレート内のトップレベルref | 不要 |
| テンプレート内のネストされたref | 必要(分割代入で回避) |
| reactiveオブジェクトのプロパティ | 不要 |
| 配列・Mapの中 | 必要 |
テンプレートの注意点:
{{ count + 1 }} <!-- ✅ トップレベルref -->
{{ object.id + 1 }} <!-- ❌ ネストされたref → [object Object]1 -->
{{ object.id }} <!-- ✅ {{ }}の最終値がrefならアンラップされる -->ネストされたrefは分割代入でトップレベルに持ち出す:
const { id } = objectKey Concepts
| 用語 | 説明 |
|---|---|
| ref() | リアクティブな状態を宣言する関数 |
| .value | refの値にアクセスする窓口(getter/setter) |
| アンラップ | refの.valueを自動で剥がして中身を取り出すこと |
| nextTick() | DOM更新が完了するまで待つAPI |
| Deep Reactivity | ネストされたオブジェクト・配列も全て変更検知される |
| reactive() | オブジェクト自体をリアクティブにする(制限あり) |
| shallow ref | .valueのアクセスのみ追跡(深い追跡を省略して最適化) |