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 } = object

Key Concepts

用語説明
ref()リアクティブな状態を宣言する関数
.valuerefの値にアクセスする窓口(getter/setter)
アンラップrefの.valueを自動で剥がして中身を取り出すこと
nextTick()DOM更新が完了するまで待つAPI
Deep Reactivityネストされたオブジェクト・配列も全て変更検知される
reactive()オブジェクト自体をリアクティブにする(制限あり)
shallow ref.valueのアクセスのみ追跡(深い追跡を省略して最適化)