echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【Vue.js】data変数の値の変更が画面に反映されない場合の対処

はじめに

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');
}