Vue基本④(リアクティブについて)
Vueについて③からの続き
ひとまずおさらいです。
Vue.jsはリアクティビティシステムになっていないJavaScriptに対して
Vueが提供している、ref関数というものを使用してデータの受け渡しを楽にします。
そのやり方は
<script setup>
import { ref } from 'vue'
let number = ref(9999)
const title = ref('Nyan programer')
const info = ref({
nyans: 100,
churu: 4
})
</script>
<template>
<h1>Title: {{ title }}</h1>
<h2>Number: {{ number - 1000 }}</h2>
<button @click="increment">ボタン</button>
<h2>Nyans: {{ info }}</h2>
<h3>Churu: {{ info.churu }}</h3>
</template>
このようにref関数をvueからimportして値にref関数を付けて引数に値、返り値にデータとして出力しています。
ref関数は技術的な問題でオブジェクトとして管理されていますのでnumber9999はオブジェクトとして扱われています。
しかし、このinfoには元々のデータがオブジェクトになっていて、わざわざオブジェクトにしなくてもこれをリアクティブにしたらいいのです。
この使い分けがVueではできます。
ref関数の他にもう一つreactive関数というのがあり、これもref関数と同じimportして使用できます。
import { ref, reactive } from 'vue'
reactive関数を使ってデータを新たに作ります。
使い方はref関数と同じようにreactive()にオブジェクトを入れます。
ちなみにこの関数はオブジェクト以外のデータを入れるとエラーになります。
const bossNyan = reactive({
name: 'Boss',
age: 10
})
ボスニャンコのデータを作ります。
このデータを取り出す際にref関数は.valueを付けたのに対しこちらは必要ありません。
const bossNyan = reactive({
name: 'Boss',
age: 10
})
console.log(bossNyan.age);
これでデータにアクセスできます。
<template>で使用する際もref()と同じで.value付けずに取り出ししたいデータにアクセスします。
<script setup>
const bossNyan = reactive({
name: 'Boss',
age: 10
})
console.log(bossNyan.name)
</script>
<template>
<h2>bossNyan:{{ bossNyan.age }}</h2>
</template>
このボスニャンのageの年齢を動的に動かしたい場合もref関数と同様に設定できます。
function increment() {
number.value += 1
bossNyan.age += 1
}
これでボタンを押すと1ずつ増えていきます。
reactive関数で作られるリアクティブなデータは元のデータがそのまま使われているかのように扱うことができます。
ref関数とreactive関数は厳密には違う構造で処理していますが、
ref関数は内部的にreactive関数を使っていてその関数の第一引数に指定されたオブジェクトをリアクティブにして、それを.valueに格納しているということになります。
簡単に言うと、ref()のオブジェクトではreactive()が働いているんだなっていう感じです。
このreactiveオブジェクトのプロパティを追加するときは
bossNyan.bio = 'nyaaaan'
このようにドットの後に新しいプロパティ名を付けて値を入れます。
もちろん、このbioはリアクティブなので
<h2>bossNyan bio:{{ bossNyan.bio }}</h2>
これで値が取れます。
下記のように関数にも追加し、ボタンを押すと表示を変えることもできます。
function increment() {
number.value += 1
bossNyan.age += 1
bossNyan.bio = 'にゃーーーーん'
}
ちなみに、このreactive関数のプロパティにさらにオブジェクトがある場合もreactive関数の中なので同じくリアクティブが適用されます。
なのでこのデータを取得することができます。
const bossNyan = reactive({
name: 'Boss',
age: 10,
place: {
park: 'sandArea',
parkingArea: 'under the car'
}
console.log(bossNyan.place)
さらにplaceの中にオブジェクトを入れても全てreactive()の中なのでリアクティブに扱われますし、同じようにドットとプロパティ名でデータを取得することができます。
function increment() {
number.value += 1
bossNyan.age += 1
bossNyan.bio = 'にゃーーーーん'
bossNyan.place.parkingArea = 'on the car'
}
<template>
<h1>Title: {{ title }}</h1>
<h2>Number: {{ number - 1000 }}</h2>
<button @click="increment">ボタン</button>
<h2>Nyans: {{ info }}</h2>
<h3>Churu: {{ info.churu }}</h3>
<h2>bossNyan age: {{ bossNyan.age }}</h2>
<h2>bossNyan bio: {{ bossNyan.bio }}</h2>
<h2>bossNyan parkingArea: {{ bossNyan.place.parkingArea }}</h2>
</template>
ボタンを押すとこのように変化します。
これでreactive関数は理解できたかと思います。
逆にreactive関数の中でref関数を使うとどうなるのか?
例えば先ほどの
const bossNyan = reactive({
name: 'Boss',
age: 10,
place: {
park: 'sandArea',
parkingArea: 'under the car'
}
に新しいプロパティeat: ref('food')を加えるとします。
const bossNyan = reactive({
name: 'Boss',
age: 10,
place: {
park: 'sandArea',
parkingArea: 'under the car'
},
eat: ref('food')
console.log(bossNyan.eat)
この場合は出力すると上記のconsole.logように.valueを省略できます。
逆にconsole.log(bossNyan.eat.value)のように.valueを付けてしまうとundefinedになります。
必ず省略しないといけません。
<template>で使用するときも、データを更新するときも同じく、reactive()の中のデータを出力する際は.valueは必要ありません。
<script setup>
function increment() {
number.value += 1
bossNyan.age += 1
bossNyan.bio = 'にゃーーーーん'
;(bossNyan.place.parkingArea = 'on the car'), (bossNyan.eat = 'catFood')
}
</script>
<template>
<h1>Title: {{ title }}</h1>
<h2>Number: {{ number - 1000 }}</h2>
<button @click="increment">ボタン</button>
<h2>Nyans: {{ info }}</h2>
<h3>Churu: {{ info.churu }}</h3>
<h2>bossNyan age: {{ bossNyan.age }}</h2>
<h2>bossNyan bio: {{ bossNyan.bio }}</h2>
<h2>bossNyan parkingArea: {{ bossNyan.place.parkingArea }}</h2>
<h2>bossNyan eat: {{ bossNyan.eat }}</h2>
</template>
ボタンを押すと変わります。
しかし、1点だけ注意があります。
代入する値がrefオブジェクトの場合、このときは.valueが自動で付いてしまうと.value.valueみたいなところに格納してしまうのでうまくアクセスできなくなります。
新しいデータがref関数の時だけVueは.valueを付けないようにしています。
なので新しいref関数のデータはそのまま置き換えられるということになります。
簡単に言うと、reactive()のオブジェクトにref()があるときはそのままの値として扱えばいいということになります。
ただし、この.valueが自動で付く動きは配列の時だけは機能しないようになっています。
例えば下記のような時
const items = reactive([ref(1), ref(2), ref(3)])
console.log(items[0]);
これも全てreactiveにはなりますが、配列の要素を取得する時はたとえそれがリアクティブオブジェクトの中のオブジェクトだったとしても.valueは自動で付きません。
上記のconsole.log(items[0]);を出力してもオブジェクトが返ってきてしまいます。
なので配列の要素に対してはリアクティブオブジェクトの中だったとしても.valueを書く必要があります。
const items = reactive([ref(1), ref(2), ref(3)])
console.log(items[0].value)
これは配列にはさまざまなメソッドがあって、配列の順を逆にしたりrefオブジェクトとそうじゃないものがあったりと複雑になってしまうので配列は.valueを書くことになっています。
あと、普通のオブジェクトとrefオブジェクトを一緒に使うときは注意が必要です。例えば、
const nyansInfo = {
chatora: ref(10),
kuro: ref(2),
mohumohu: ref(5)
}
このように普通のオブジェクトの中にrefオブジェクトが入っている場合、
<template>で使用する際は各プロパティに.valueを付ける必要があります。
<h2>bossNyan: {{ nyansInfo.chatora.value }}</h2>
<h2>bossNyan Chatora: {{ nyansInfo.chatora + 1 }}</h2>
Vueでは一番左の値(先頭)しか監視しないので、そこがrefオブジェクトの場合は自動で.valueを付けますが、普通のオブジェクト内にrefがある場合は.valueを付けないと表示がobject表記になってしまいます。
上記のコードの表示はこのようになります。
かなりややこしい話ですが、<template>タグ内の{{}}は最終的にref関数として扱われるので、+1が無ければ.value付けなくても表示はされますが、明示的に付けておくことをおすすめします。
.valueが付いていたら先頭は普通のオブジェクトというのもわかるし、<template>の修正もわかりやすいので。
reactive関数の中のref関数を<template>タグで使用する際は.valueいらなくて、
普通のオブジェクト内のref関数を<template>タグで使用する際は.valueが必要ということです。
ちなみに先ほどのreactiveオブジェクト内のref関数は例外で、<template>タグ内でも.valueは必要ありません。
これはVueがreactive関数も監視してくれているからですね。
これでref関数、reactive関数の使い方は一通り終わります。
長々と書いてしまいましたが、基礎的でかなり重要なので。
次回からはVue.jsの基礎構文などを書いていきます。
Discussion