こんにちは!
今日は、Composition APIでリアクティブな状態管理として使用されるref
とreactive
についてまとめたいと思います!
「ref
とreactive
の使い分けはプリミティブな値か否かである」くらいの知識だったので、改めて言語化したいと思います。
ref()を使ったリアクティブな状態管理
refは、リアクティブなプリミティブ値を管理するために使用されるものです。
<script setup>
import { ref } from 'vue'
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
</script>
ref関数は引数を受け取り、それを.valueプロパティを持つオブジェクトにラップして返します。
これにより、Vueは.valueへのアクセスと変更を検出し、変更を追跡しながらその変更をマスタッシュ構文({{ }}
)として使用しているtemplate
内の表記や李アクティブな変数に反映します。
テンプレート内での使用方法
コンポーネントのテンプレート内でrefにアクセスするためには、setup関数で宣言し、それを返します。
(<script setup>
で簡略化しない場合の使い方の方がイメージしやすいと思ったため、あえて簡略化していません。)
<script setup>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
}
}
</script>
<template>
<div>{{ count }}</div>
</template>
テンプレート内では.valueを付ける必要はなく、直接refを使用できます。
理由としては、refはテンプレート内で使用されると自動的にアンラップしてくれるからです。
また、イベントハンドラー(v-on:clickなど)でもrefを変更することが可能です。
<script setup>
const count = ref(0)
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
reactive()を使ったオブジェクトのリアクティブ化
reactiveは、オブジェクト全体をリアクティブにするための関数です。
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
</script>
<template>
<button @click="state.count++">{{ state.count }}</button>
</template>
reactiveはオブジェクトを深くリアクティブにします。
ネストしたオブジェクトも自動的にリアクティブにしてくれます。
refとreactiveの使い分け
- ref
- プリミティブ値や単一のオブジェクトをリアクティブにするのに適している
- reactive
- 複数のプロパティを持つ複雑なオブジェクトをリアクティブにするのに適している(※)
※ 2024/6/19にコメントを頂きました!
複数のプロパティを持っていたり、ネストしているオブジェクトに対してもref
はリアクティブに動作することができます。
そのため、2024/06/19時点でreactive
を使用するメリットは.value
を1つ省略するくらいのメリットしかないため、基本的にrefを使うで問題ありません。
.value
を1つ省略できるについて下記に簡単なコードを書いたので、ぜひ参考にしてみて下さい。
refの場合
<template>
<div>
<p>Age: {{ state.user.age }}</p>
<button @click="incrementAge">Increment Age</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const state = ref({
user: {
name: 'Qiita',
age: 25
}
});
const incrementAge = () => {
state.value.user.age++;
}
</script>
reactiveの場合
<template>
<div>
<p>Age: {{ state.user.age }}</p>
<button @click="incrementAge">Increment Age</button>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const state = reactive({
user: {
name: 'Qiita',
age: 25
}
});
const incrementAge = () => {
state.user.age++;
}
</script>
refをオブジェクトのプロパティとして使用
ref
をreactive
オブジェクトのプロパティとして使用する場合、そのプロパティは自動的にアンラップされます。
<script setup>
const count = ref(0)
const state = reactive({ count })
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
</script>
DOMの更新をハンドリングする
リアクティブな値を変更するとDOMは自動的に更新されますが、この更新処理は非同期的に動きます。
そのため、DOMの更新前にレンダリングがはしってしまう(適切な構造ではないDOM構造が完成してしまう。)
その場合は、nextTick()
というグローバルAPIをしようすることで、DOMの更新を待つことができます。
もし、意図したDOM構造になっていないときは、リアクティブな値の更新後、DOMの更新が完了されることを待たずにレンダリング処理が非同期的に走ってしまっていないかどうか確認することが大切です。
<script setup>
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// DOM が更新されました
}
</script>
まとめ
すこしでもリアクティブシステムについての解像度が上がれば幸いです。
ここまで読んでくださり、ありがとうございました!