テクメモ

備忘録

Vue3 Composition APIのReactivity APIまとめ

vue3のComposition APIのrefとreactiveについて調べた際に、他のReactivity APIについても気になったので今回まとめました。
自分が理解できた箇所のみをまとめた浅い記事ですがご容赦ください。

reactive

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

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

obj.count++

console.log(obj.count) // 2

ref

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

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

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

computed

ゲッター関数を受け取り、ゲッターからの戻り値に対して不変のリアクティブなrefオブジェクトを返す関数です。

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2
count.value++
console.log(plusOne.value) // 3

plusOne.value++ // error

ゲッター関数とセッター関数を使えば、書き込み可能なrefオブジェクトを作成することもできます。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 100,
  set: val => {
    count.value = val - 1
  }
})

console.log(plusOne.value) // 101
plusOne.value = 100
console.log(count.value) // 99

readonly

オブジェクトまたはrefを受け取り、読み取り専用プロキシを返す関数です。
ネストされたプロパティも読み取り専用になるようです。

const original = reactive({ count: 0 })
const copy = readonly(original)

original.count++
console.log(copy.count) // 1

// 読み取り専用だから書き換えられない
copy.count++ // error

watchEffect

依存関係を事後的に追跡しながら関数をすぐに実行し、依存関係が変更されたときに関数を再実行します。

const count = ref(0)

watchEffect(() => console.log(count.value)) // 0

setTimeout(() => {
  count.value++ // 1
}, 100)

watch

Vue2でのwatchとほぼ変わらないようです。

const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count) => {
    sampleFunction(count)
  }
)

複数を監視対象にすることもできるようです。

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})

unref

引数がrefの場合は内部値を返し、それ以外の場合は引数自体を返す関数です。

const refSample = ref(100)
const notRefSample = 10

unref(refSample) // 100
unref(notRefSample) // 10

toRef

リアクティブなオブジェクトのある特定のプロパティをrefオブジェクトに変換する関数です。

const state = reactive({
  hoge: 1,
  fuga: 2
})

const sampleRef = toRef(state, 'hoge')

sampleRef.value++
console.log(state.hoge) // 2

state.hoge++
console.log(sampleRef.value) // 3

toRefs

リアクティブなオブジェクトの全てのプロパティをrefオブジェクトに変換する関数です。

const state = reactive({
  hoge: 1,
  fuga: 2
})

const sampleRefs = toRefs(state)

state.hoge++
console.log(sampleRefs.hoge.value) // 2

sampleRefs.hoge.value++
console.log(state.hoge) // 3

sampleRefs.fuga.value++
console.log(state.fuga) // 3

isRef

値がrefオブジェクトであるかどうかを確認する関数です。

const sampleRef = ref(1)
const sampleReactive = reactive({hoge: 1})
const sample = 1

console.log(isRef(sampleRef)) // true
console.log(isRef(sampleReactive)) // false
console.log(isRef(sample)) // false

isReactive

値がreactiveによって作成されたProxyオブジェクトであるかどうかを確認する関数です。
readonlyによってラップしたreactiveオブジェクトでもtrueを返すようです。

const sampleRef = ref('hoge');
const sampleReactive = reactive({
  fuga: 'fuga'
});
const sample = 'piyo'
const sampleRefAsReadonly = readonly(sampleRef);

isReactive(sampleRef) // false
isReactive(sampleReactive) // true
isReactive(sample) // false
isReactive(sampleRefAsReadonly) // true

isReadonly

値がreadonlyかどうかを確認する関数です。

const sampleRef = ref("hoge");
const sampleReactive = reactive({
  fuga: "fuga"
});
const sampleReadonly = readonly(sampleReactive);

isReadonly(sampleRef) // false
isReadonly(sampleReactive) // false
isReadonly(sampleReadonly) // true

customRef

更新のトリガーを明示的に制御し、カスタムしたrefオブジェクトを返すことができる関数らしいです。

const outputLogRef = (value: string) => {
  return customRef((track, trigger) => ({
    get() {
      console.log(value)
      track()
      return value
    },
    set(newValue: string) {
      console.log(newValue)
      value = newValue
      trigger()
    }
  }))
}

const hoge = outputLogRef("")

hoge.value = "hoge" // hogeが出力される
hoge.value = "fuga" // fugaが出力される
const fuga = hoge.value // fugaが出力される

markRaw

オブジェクトをマークして、reactiveなProxyオブジェクトに変換しないようにする関数らしいです。

const hoge = markRaw({})
console.log(isReactive(reactive(hoge))) // false

shallowReactive

ネストされたプロパティ以外をリアクティブにして返す関数です。

const state = shallowReactive({
  hoge: 1,
  fuga: {
    piyo: 2
  }
})

// これはリアクティブ
isReactive(state.hoge)
// ネストされたプロパティはリアクティブじゃない
isReactive(state.fuga) // false

shallowReadonly

ネストされたプロパティ以外をreadonlyにして返す関数です。

const state = shallowReadonly({
  hoge: 1,
  fuga: {
    piyo: 2
  }
})

// これはreadonly
isReadonly(state.hoge)
// ネストされたプロパティはreadonlyじゃない
isReadonly(state.fuga) // false

toRaw

reactiveまたはreadonlyオブジェクトの元のオブジェクトを返す関数です。

const sample = {}
const reactiveSample = reactive(sample)

console.log(toRaw(reactiveSample) === sample) // true

終わりに

Compotion APIのReactivity APIをまとめてみました。
Vue2のComposition APIプラグインではまだ対応しておらず、全く見たことないものや
いつ使うのか全く分からないものもありましたが、適宜用途に合わせて使っていきたいと思います。

参考文献

API Reference | Vue Composition API

CompositionAPIのwatchとwatchEffectの違い - Qiita