テクメモ

備忘録

Vue3 Composition APIのrefとreactiveをざっくりと理解する

導入

今週、社内でVue3についての勉強会がありました。
その中で、一番気になったComposition APIのrefとreactiveについて調べることにしました
自分が理解できた箇所のみをまとめた浅い記事ですがご容赦ください。

リアクティブとは?

refとreactiveの話をする前に、リアクティブとは何かということについて理解する必要があります。
リアクティブとは、「ある変数を書き換えた際に既に定められた関係性によって、他の変数が更新される / 事前に定めた動作が実行される」だと僕は理解しています。

簡単な例だと以下になります。

const sample = reactive({a: 1})
const sample2 = computed(() => sample.a + 100)

console.log(sample2.value) // 101

sample.a = 10

console.log(sample2.value)// 110

リアクティブなデータなので、sample.aを書きかえた際にsample2が更新されています。

refとreactiveの基本

ref

プリミティブな値をアクティブで可変なrefオブジェクトを返す関数です。 refオブジェクトにはvalueというプロパティがあり、これによってアクセスできるようです。

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

template内で使う場合は、refがレンダーコンテキストのプロパティとして返されるようで、valueプロパティでアクセスする必要はないようです。

<template>
  <div>{{ count }}</div>
</template>

<script>
  export default {
    setup() {
      return {
        count: ref(0)
      }
    }
  }
</script>

reactive

オブジェクトをリアクティブなProxyオブジェクトにして返す関数です。 この際のリアクティブへの変換はディープコピーのようですが、返されるプロキシは元のオブジェクトと同一ではないようです。

    const obj = reactive({count: 1})
    console.log(obj.count) // 1

    obj.count++

    console.log(obj.count) // 2

refとreactiveの違い

リアクティブ化できる値

reativeはプリミティブな値はリアクティブにできませんが
refは、プリミティブな値以外(配列やオブジェクト等)でもリアクティブにできるようです。

// reactiveはプリミティブな値をリアクティブにすることはできない
    const number = reactive(0)
    console.log(number) // undefined
    
// refはプリミティブな値以外(配列やオブジェクト等)でもリアクティブにできる
    const str = ref('hoge')
    const array = ref([1, 2, 3, 4])
    const obj = ref({a:1, b:1})

    console.log(isRef(str)) //true
    console.log(isRef(array)) //true
    console.log(isRef(obj)) //true

    console.log(obj.value.a) // 1
    obj.value.a = 100
    const sample =  obj.value.a = 100 + 1
    console.log(sample) // 101

リアクティブの消失

reactiveでリアクティブにしたデータを、リアクティブのまま分割して使うことは出来ないようです。 文章だとわかりにくいので、以下にサンプルのコードを書きます

count.ts(reactiveなオブジェクトをセットする関数)

import { reactive } from '@vue/composition-api'

export function setCountReactive() {
  const obj = reactive({
    count: 1
  })
  return obj
}

home.vue

<template>
  <div>
    <button @click="incrementA()">足す</button>
    {{ obj.count }}
    <button @click="incrementB()">足す</button>
    {{ count }}
  </div>
</template>

<script lang="ts">
import { setCountReactive } from './count'

export default {
  setup() {
    /// 通常パターン
    const obj = setCountReactive()

    function incrementA() {
      return obj.count++
    }

    // リアクティブが消失してしまうパターン
    let { count } = setCountReactive()

    function incrementB() {
      return count++
    }

    return {
      incrementA,
      incrementB,
      obj,
      count,
    }
  }
}
</script>

こういう場合に、リアクティブに扱いたい場合はToRefsを仕様して、refでラップしてあげればいいようです。

count.ts(reactiveなオブジェクトをセットする関数)

import { reactive, toRefs } from '@vue/composition-api'

export function setCountReactive() {
  const obj = reactive({
    count: 1
  })
  return toRefs(obj)
}

ただし、refでラップする場合はvalueプロパティでアクセスする必要があります。
home.vue

<template>
  <div>
    <button @click="incrementA()">足す</button>
    {{ obj.count }}
    <button @click="incrementB()">足す</button>
    {{ count }}
  </div>
</template>

<script lang="ts">
import {setCountReactive} from './count'

export default {
  setup() {
    /// 通常パターン
    const obj = setCountReactive()

    function incrementA() {
      return obj.count.value++
    }

    // リアクティブが消失してしまうパターン
    let {count} = setCountReactive()

    function incrementB() {
      return count.value++
    }

    return {
      incrementA,
      incrementB,
      obj,
      count,
    }
  }
}
</script>

refとreactiveの使い分け

ここまで調べて、refとreactiveはどのように使い分けるのか?という疑問がわきました。
まだ有用な使い分けの仕方はわかりませんが、refとreactiveの違いでも書いたように
プリミティブな値を使う場合はref、それ以外の場合はreactiveを使っておけば間違いはないのかなと思います。
ただ、「オブジェクトのある特定のプロパティだけリアクティブに扱いたい」などの場合はreactiveでtoRefsを使えばいいようです。

終わりに

今回は、refとreactiveについて書きました。
Vue3での変更点や便利そうな新機能など、まだまだキャッチアップできていない情報がたくさんあるので 今後も機会があれば、アウトプットしていきたいと思います。

参考

API Reference | Vue Composition API

Ref vs Reactive Vue3 Composition APIのリアクティブ関数の探究 / ref vs reactive Vue Composition API Deep in - Speaker Deck