Vue.jsにおけるオブジェクトと配列は普通の変数とは挙動が違って手こずったので
しっかりと調べ直してみました
オブジェクトにdata()
で定義していないプロパティを後から追加してもリアクティブにならない場合がある
リアクティブにならないプロパティ追加
template部分
<template>
{{ obj }}
<button @click="updateObjDefault">updateObjDefault</button>
<button @click="updateObjAdditional">updateObjAdditional</button>
</template>
script部分
<script>
data() {
return {
obj: { default: 1 }
}
},
methods: {
updateObjDefault() {
// templateに即時反映される
this.obj.default += 1;
},
updateObjAdditional() {
// templateに即時反映されない
this.obj.additional += 1;
}
},
created() {
this.obj.additional = 1;
// ↓これもダメ
// this.obj = Object.assign(this.obj, { additional: 1 });
}
</script>
みたいにすると
ブラウザでページ開いたときに
{ "default": 1, "additional": 1 }
と初期表示はされるがadditional
の方はリアクティブになっていないので
クリックイベント(updateObjAdditional
)でobj.additional
の値が変更されたとしてもビューに反映されない
- ※他のトリガーによる再描画のタイミングでやっと反映される
- ※
mounted()
でプロパティ追加すると"additional": 1
は初期表示すらされない-
created()
はレンダリングの前、mounted()
はレンダリングされたあとに実行されるため
-
リアクティブになるプロパティ追加
script
<script>
created() {
this.$set(this.obj, 'additional', 1);
// もしくは
// this.obj = Object.assign({}, this.obj, { additional: 1 });
}
</script>
みたいにすると
ブラウザを開いたときに
{ "default": 1, "additional": 1 }
という初期表示で、additional
もリアクティブになっているので
クリックイベント(updateObjAdditional
)でobj.additional
の値が変更されると
ビューに即時反映(リレンダリング)される
- ※この方法でプロパティ追加すると追加や変更のタイミングで再描画される
- リアクティブになるので
mounted()
でやってもadditional
が表示され、変更も即時反映される
- リアクティブになるので
-
this.$set()
やObject.assign()
の説明は公式ドキュメントのこの辺とかこの辺とか
配列の「中」はリアクティブではない
リアクティブに反映されないパターン
template
<template>
{{ arr }}
<button @click="updateArrFirst">updateArrFirst</button>
<button @click="updateArrSecond">updateArrSecond</button>
</template>
script
<script>
data() {
return {
arr: [1]
}
},
methods: {
updateArrFirst() {
this.arr[0] += 1;
},
updateArrSecond() {
this.arr[1] += 1;
}
},
created() {
this.arr[0] += 1;
this.arr[1] = 1;
}
</script>
みたいにすると
ブラウザを開いたときに
[ 2, 1 ]
という初期表示になるが配列の中身はリアクティブじゃないので
クリックイベントなどで代入による要素上書きや追加をしてもリアクティブにビューに反映されない
- ※リアクティブになっていないオブジェクトのプロパティと同様に他のトリガーによる再描画のタイミングでやっと反映される
-
mounted()
で上書きと追加を行うと初期表示にすら反映されない
-
リアクティブに反映されるパターン
template
<template>
<li>{{ arr }}</li>
<button @click="updateArrFirst">updateArrFirst</button>
<button @click="updateArrSecond">updateArrSecond</button>
</template>
script
<script>
data() {
return {
arr: [1]
}
},
methods: {
updateArrFirst() {
// this.arr[0] += 1 をリアクティブに反映させる書き方 ※1
this.arr[0] = [this.arr[0] + 1, this.arr[1]];
},
updateArrSecond() {
// this.arr[1] += 1 をリアクティブに反映させる書き方 ※2
// indexが1(2番目)の要素から1つ取り除き、`this.arr[1] + 1`を挿入する
this.arr.splice(1, 1, this.arr[1] + 1);
}
}
created() {
// this.arr[0] += 1 をリアクティブに反映させる書き方 ※3
this.$set(this.arr, 0, this.arr[0] + 1);
// this.arr[1] = 1 をリアクティブに反映させる書き方 ※2
this.arr.push(1);
}
</script>
- ※1 配列ごと上書きする
- コピーして変更して丸ごと上書きとか
- 中の要素はリアクティブではないが配列そのものはリアクティブなので上書きする代入で再描画される
- 公式ドキュメント
- ※2
splice()
,push()
などリレンダリングのトリガーになってるメソッドを使う- 詳しい説明やその他のメソッドは公式ドキュメントに
- ※3
this.$set()
を使って代入することでリレンダリングさせる- ただしオブジェクトと違い
this.$set
してもそれ以降リアクティブになるわけではない - あくまでリレンダリングのトリガーとして使う
- 公式ドキュメントとか
- ただしオブジェクトと違い
入れ子(配列の中のオブジェクト、オブジェクトの中の配列)の場合も
同じ
参考資料
公式の