vue.jsでオブジェクトに新しくプロパティを追加してもリアクティブに動作しません。
未定義のプロパティをv-ifで参照させた時に、この事象に遭遇したため、調査してまとめました。
以下の3つのケースの変数を、v-ifに指定した場合の挙動について見ていきましょう。
- プリミティブな変数
- オブジェクトのプロパティ(定義済)
- オブジェクトのプロパティ(未定義)
例1:プリミティブな変数をv-ifに指定した場合
<template>
  <div>
    <button @click="show">表示する</button>
    <p v-if="isShow">クリックされました</p>
  </div>
</template>
<script>
export default {
  data () {
    return {
      isShow: false,
    }
  },
  methods: {
    show () {
      this.isShow = true
    }
  }
};
</script>
このケースの場合は、ボタンをクリックすると、期待通りクリックされましたの文言が画面に表示されます。
例2:オブジェクトのプロパティ(定義済)をv-ifに指定した場合
<template>
  <div>
    <button @click="show">表示する</button>
    <p v-if="hoge.isShow">表示</p>
  </div>
</template>
<script>
export default {
  data () {
    return {
      hoge: {
        isShow: false,
      }
    }
  },
  methods: {
    show () {
      this.hoge.isShow = true
    }
  }
};
</script>
この場合も、ボタンをクリックすると、期待通りクリックされましたの文言が画面に表示されます。
例3:オブジェクトのプロパティ(未定義)をv-ifに指定した場合
<template>
  <div>
    <button @click="show">表示する</button>
    <p v-if="hoge.isShow">表示</p>
  </div>
</template>
<script>
export default {
  data () {
    return {
      hoge: {
        // isShow: false, コメントアウトで未定義にしています
      }
    }
  },
  methods: {
    show () {
      this.hoge.isShow = true
    }
  }
};
</script>
この場合、初期表示時点では期待通り、文言が表示されていません。(コンソールにもエラーは吐かれません。)
この場合、this.hoge.isShow = trueの処理が実行されるのですが、クリックされました。の文言は表示されません。解説・理由は以下の通りです。
オブジェクトのプロパティを追加してもリアクティブに動作しない
Vueはコンポーネント生成時(onCreate時)のデータに対して変更検出を行うため、生成時に未定義のプロパティが後から追加されても、リアクティブなプロパティとして動作させることができません。
https://vuejs.org/v2/guide/reactivity.html#For-Objects
# For Objects
Vue cannot detect property addition or deletion. ...
解決策
例3のような、オブジェクトのプロパティ追加時でもリアクティブに動作させるには、Vue.setを利用します。
- Vue.set(object, propertyName, value)の使用
- 第1引数: 追加したいオブジェクト
- 第2引数: 追加したいプロパティ名
- 第3引数: プロパティの値
 
Vue.setのエイリアスのthis.$setを利用して、例3の場合でもリアクティブに動作するよう実装してみる。
<template>
  <div>
    <button @click="show">表示する</button>
    <p v-if="hoge.isShow">表示</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      hoge: {
        // isShow: false, コメントアウトで未定義にしています
      }
    };
  },
  methods: {
    show() {
      this.$set(this.hoge, "isShow", true);
    }
  }
};
</script>
this.hogeにisShow (true)が追加されたことをVueが検知できるので、クリックされました。の文言が無事に表示されました。🙌
補足
Vue3では、オブジェクトのプロパティ追加時でもリアクティブに動作する新仕様になっているっぽいので、技術調査したら改めて記事にしようと思います。

