はじめに
Vue.jsでは、data()関数の戻り値に設定したオブジェクトのキー(以下「data変数」と記載)について、method内の各関数などで値を変更すると、それが画面に即座に反映されます。
<script> export default { data() { return { hoge: 'aaa' } }, methods: { example(val) { // valの値が即座に画面に反映される。 this.hoge = val; } } } </script>
しかし、一部のケースで値の変更が画面に反映されないケースがあります。
そのケースの説明と対処方法です。
参考サイト(どちらもVue.js公式サイト)
オブジェクトと配列
上の題にもある通り、オブジェクトと配列は、変更が画面に反映されないケースがあります。
オブジェクト
オブジェクトについて、下記のように「data変数で定義したオブジェクトにキーを追加(または削除)した」場合、それは画面に反映されません。(ただし、data変数で定義済のキーの値の変更は、即座に反映されます)
なお、キーの追加や削除をした場合もあくまで「即座に画面に反映されない」だけで、追加や削除自体は有効になっていますので、そのあとに何かしら画面の更新が行われた場合は、ちゃんとそれらは画面上の値にも反映されます(これは配列も同様です)。
<script> export default { data() { return { hoge: { foo: 'bar', foofoo: 'barbar' } } }, methods: { example(val) { // キーを新たに追加しても、それは画面に反映されない。 this.hoge.fuga = val; // キーの削除も同様 delete this.hoge.foofoo; // data変数で定義済みのキーの値の変更はOK。 // この時点で、上記のキーの追加/削除が画面に反映される。 // 画面にはthis.hoge = {foo: val, fuga: val} の状態で表示される this.hoge.foo = val; } } } </script>
Vue.jsでキーの追加(または削除)を即座に画面に反映する場合、「Vue.set」または「vm.$set(this.$set)」関数を使う必要があります。(vm.$setはVue.setのエイリアス)
<script> methods: { example(val) { // これならOK。 // ちなみに引数は順に「data変数名」「追加するキー名」「該当キーの値」 this.$set(this.hoge, 'fuga', val); } } } </script>
また、複数のキー&値を割り当てたい場合、下記のようにObject.assign()の戻り値をdata変数に代入すればOKです。(Object.assign()単体ではダメ)
<script> methods: { example() { // こんな感じ。 // Object.assign単体ではなく、戻り値を代入する。 this.hoge = Object.assign({}, this.hoge, { fuga: fuga, barbar: 'barbar', piyopiyo: 'piyopiyo' }); // 下記はNG。(画面に反映されない) // Object.assign(this.hoge, { fuga: fuga, barbar: 'barbar', piyopiyo: 'piyopiyo' }); } } } </script>
配列
配列については、下記の変更は検知できません。
- 配列の(特定インデックスの)値の変更
- 配列の長さ(length)のみの変更(下記参照)
- push/popなどで要素数(=値)の追加/削除などを行った場合は、反映されます。
<script> export default { data() { return { hoge: ['foo', 'bar', 'fuga']; } }, methods: { example(val) { // 以下は、反映されない。 // 配列の(特定インデックスの)値の変更 this.hoge[1] = val; // 配列の要素数(length)のみの変更 this.hoge.length = 6; // 配列操作関数(Array.prototype.XXX)を使った配列の要素数の // 追加、変更などはOK this.hoge.pop(); } } } </script>
配列の変更を即座に反映するには(すでに若干ネタバレしてますが)、下記のような配列操作関数(Array.prototype.XXX)を使います。
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
また、先述の「Vue.set」または「vm.$set(this.$set)」関数でもできます。
<script> export default { data() { return { hoge: ['foo', 'bar', 'fuga']; } }, methods: { example(val) { // 「Vue.set」または「vm.$set(this.$set)」関数を使う場合 // 引数は順に「data変数名」「値を変更するindex」「該当indexの値」 this.$set(this.hoge, 1, val); // 配列操作関数を使う場合 // なお公式ページ、および各種ブログなどでは // splice関数が推奨されている。 // splice関数については後述 this.hoge.splice(1, 1, val); // 配列の要素数(length)のみを変える場合 this.hoge.splice(this.hoge.length, 0, "", "", ""); } } } </script>
splice関数について
公式ページ、および各種ブログなどで推奨されているsplice関数ですが、下記の関数になります。(ちなみに僕は、この現象を調査するまで、splice関数の存在自体知りませんでした)
splice(start, deleteCount, item1, item2, ...itemN)
引数 | 説明 | 備考 |
---|---|---|
start | 操作(追加/削除等)を行う基準となる位置(=index) | |
deleteCount | 削除する要素数 | 省略可 |
item1, item2, ...itemN | 追加する要素(複数指定可能) | 省略可 |
【参考】 Array.prototype.splice() - JavaScript | MDN
例として、下記ソースは以下のように動作します。
- startが1なので、index=1の場所(「fuga」の場所)を基準位置とする。
- deleteCountが2なので、index=1の場所から要素を2つ削除する。(「fuga」「piyo」が削除される)
- itemに'foo', 'bar', 'foofoo'の3つが指定されているので、index=1の位置に順に'foo', 'bar', 'foofoo'の3つを挿入する。
- 最終的に、arrayは['hoge', 'foo', 'bar', 'foofoo', 'hogehoge']となります。
example(val) { const array = ['hoge', 'fuga', 'piyo', 'hogehoge']; array.splice(1, 2, 'foo', 'bar', 'foofoo'); }
なお、deleteCountは省略可ですが、値の追加のみを行う場合に省略すると分かりにくいので、明示的に0を指定した方が良いと思います。(特に数値を追加する場合)
またsplice関数を下記のように使うことで、startに指定したindexの値の変更を行うこともできます。(実際はdelete→insertですが)
example(val) { // index=1の値(=fuga)を「foofoo」に変更(delete→insert)する const array = ['hoge', 'fuga', 'piyo', 'hogehoge']; array.splice(1, 1, 'foofoo'); }