【Vue.js】監視プロパティとは?

監視プロパティ

特定のデータないし算出プロパティの状態を監視することで、変化時に登録しておいた処理を自動で実行できるプロパティのこと。
ウォッチャとも呼ばれる。
使い方をコードで確認する。
HTML

<div id="vm">
  <p>
    <input type="text" v-model:value="message">
  </p>
  <p>{{ message }}</p>
  <pre>{{ $data }}</pre>
</div>

Javascript

var vm = new Vue({
  el: '#vm',
  data: {
    message: 'Hello Vue.js!'
  },
  watch: {
    message: function(newValue, oldValue) {
      console.log('new: %s, old: %s', newValue, oldValue)
    }
  }
})

watchのmessageプロパティのfunctionでnewValueとoldValueを設定することで、コンソールに変化前と変化後の値が表示される。

console.logの%sはstr型の出力を意味する。
%dはint型、%floはfloatの出力となる。

算出プロパティとの違い

苗字と名前を入力すると氏名が表示されるアプリで検証してみる。
HTML

<div id="vm">
  <p>苗字: <input type="text" v-model:value="lastName"></p> => 入力:優しい
  <p>名前: <input type="text" v-model:value="firstName"></p>  => 入力:ゴリラ
  <p>氏名: {{ fullName }}</p> => 優しい ゴリラ
</div>

監視プロパティ

var vm = new Vue({
  el: '#vm',
  data: {
    firstName: '',
    lastName: '',
    fullName: ''
  },
  watch: {
    firstName: function(value) {
      this.fullName = this.lastName + ' ' + value
    },
    lastName: function(value) {
      this.fullName = value + ' ' + this.firstName
    }
  }
})

算出プロパティ

var vm = new Vue({
  el: '#vm',
  data: {
    firstName: '',
    lastName: ''
  },
  computed: {
    fullName: function() {
      return this.lastName + ' ' + this.firstName
    }
  }
})

明らかに算出プロパティの方が短くなる。
コンパクトにまとめたい時は産出プロパティの方がベター。

監視プロパティのオプション

deep

ネストされたオブジェクトも監視することができる。
例えばメニューをコンソールから変更した場合を考える。
Javascript

var vm = new Vue({
  el: '#vm',
  data: {
    menus: [
      {name: 'Hamburger'},
      {name: 'Pasta'},
      {name: 'Sandwich'}
    ]
  },
  watch: {
    menus: {
      handler: function(newValue, oldValue) {
        console.log('Update!')
      },
      deep: true
    }
  }
})

HTML

<div id="vm">
  <ul>
    <li v-for="menu in menus">
      {{ menu.name }}
    </li>
  </ul>
</div>

menusのHamburger, Pasta, Sandwichがリスト表示される。
コンソールでメニューを一部変えてみる。

vm.menus[1].name = 'Onigiri'

するとコンソールにUpdate!が表示され、そのあとOnigiriが表示される。
handlerで得られる引数は新しい値になってしまうので注意。
Javascript

var vm = new Vue({
  el: '#vm',
  data: {
    menus: [
      {name: 'Hamburger'},
      {name: 'Pasta'},
      {name: 'Sandwich'}
    ]
  },
  watch: {
    menus: {
      handler: function(newValue, oldValue) {
        console.log('Update!')
        console.log('new: %s, old: %s',
          JSON.stringify(newValue, null, '\t'),
          JSON.stringify(oldValue, null, '\t'))
      },
      deep: true
    }
  }
})

JSON.stringifyはJavascriptの値をJSON形式に変換することができる。
これで先ほどのようにコンソールで値を変更すると、こう表示される。

new: [
	{
		"name": "Hamburger"
	},
	{
		"name": "Onigiri"
	},
	{
		"name": "Sandwich"
	}
], oldValue: [
	{
		"name": "Hamburger"
	},
	{
		"name": "Onigiri"
	},
	{
		"name": "Sandwich"
	}
]

newもoldも2つ目が’Onigiri’になってしまっている。
handlerで得られた引数が新しい値に変更されていることがわかる。

immediate

初期読み込み時にも呼び出すことができる。
先ほどのコードにimmediateオプションをつけてみる。

var vm = new Vue({
  el: '#vm',
  data: {
    menus: [
      {name: 'Hamburger'},
      {name: 'Pasta'},
      {name: 'Sandwich'}
    ]
  },
  watch: {
    menus: {
      handler: function(newValue, oldValue) {
        console.log('Update!')
        console.log('new: %s, old: %s',
          JSON.stringify(newValue, null, '\t'),
          JSON.stringify(oldValue, null, '\t'))
      },
      deep: true,
      immediate: true
    }
  }
})

コンソールで確認するとこのような表示がされる。

Update!
new: [
	{
		"name": "Hamburger"
	},
	{
		"name": "Pasta"
	},
	{
		"name": "Sandwich"
	}
], old: undefined