基礎からメモ: Vue.js CH4-p128 ウォッチャーの使い方

コンポーネントwatchオプションに、監視するデータの名前と、変化した時に呼び出されるハンドラ関数を定義する。新旧データ比較のために、ハンドラの引数として第一引数に「新い値」、第二引数に「古い値」を受け取れる。

基本的な構文はこんな感じ。

var app = new Vue({
  el: '#app',
  data: {
      ................................
  },
  watch: {
    監視するデータ: function(新しい値, 古い値) {
      // value が変化した時に行いたい処理
    },
    'item.value': function(newVal, oldVal) {
      // オブジェクトのプロパティも監視できる。
    }
  }
})

ウォッチャーのオプション

プロパティ 説明
deep Boolean ネストされたオブジェクトも監視する
immediate Boolean 初期読み込み時にも呼び出す

これらのオプションを使うには、監視するデータ名をキーとするオブジェクトにまとめる必要がある。その場合のハンドラーはhandlerプロパティに定義する。

var app = new Vue({
  el: '#app',
  data: {
      ................................
  },
  watch: {
    list: {
      handler: function(newVal, oldVal) {
        // list が変化した時に行いたい処理
      },
      deep: true,
      immediate: true
    }
  }
})

deepオプションが有効な場合、例えば配列要素内のプロパティが更新されたときにも呼び出される。

this.list[0].name = "NewValue!"

インスタンスメソッドにウォッチャーを登録する

this.$watchを使うことで、インスタンスメソッドにウォッチャーを登録することができる。

created: function() {
  this.$watch('value', function(newVal, oldVal) {
    // ..................
  })
}

インスタンスメソッドの場合は、watchオプションでの登録とは少し書き方が違い、引数に次の情報を渡す。

[
  監視するデータ,
  ハンドラ,
  オプションオブジェクト  // 任意
]

例:createdフックでウォッチャーを登録する場合

created: function() {
  this.$watch('value', function() {
    // ..................
  }, {
    immediate: true
  })
}

ウォッチャーの解除

インスタンスメソッドで登録した場合は、戻り値を使って監視の解除が可能。

var unwatch = this.$watch('value', handler)
unwatch() // valueの監視を解除

この仕組みを使って、一度だけ動作するウォッチャーを定義できる。

var unwatch = this.$watch('list', function() {
  // listが編集されたことを記録する
  this.edited = true
  // 監視を解除
  unwatch()
}, { deep: true })

実行頻度の制御

フォーム入力などで監視対象の値が高頻度で変化する場合、非同期通信などのコストの高い処理を呼び出すとパフォーマンスが低下する。 setTimeoutLodashライブラリなどを使って、ウォッチャーの実行頻度を制御できる。

debounceLodashのメソッド。実行から指定ミリ秒後にコールバックを呼び出す。

watch: {
  value: _.debounce(function(newVal) {
    // コストの高い処理
  },
  // valueの変化が終わるのを待つ時間をミリ秒指定
  500 )
}

複数の値を監視する

widthまたはheightが変化した時など複数の値を監視する場合、インスタンスメソッドを使って監視対象のデータをそれら複数の値を返す関数にして登録する。

this.$watch(function() {
  return [this.width, this.height]
}, function() {
  // width または height が変化した場合の処理
})

この監視対象を返す関数の部分を算出プロパティにすることで、よりすっきりと合理的に記述できる。

computed: {
  watchTarget: function() {
    return [this.width, this.height]
  }
},
watch: {
  watchTarget: function() { ...... }
}

フォームを監視してAPIからデータを取得する

フォームのセレクタで文字列(Vue.js 又は jQuery)が選ばれると、その変化に応じてGitHubAPIからトピックのリポジトリを検索する例。

<body>
  <div id="app">
  <!-- フォームを監視してAPIからデータを取得する  -->
    <select v-model="current">
      <option v-for="topic in topics" v-bind:value="topic.value">
        {{ topic.name }}
      </option>
    </select>
    <div v-for="item in list">{{ item.full_name }}</div>
    <pre>{{ $data }}</pre>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
  <script src="main.js"></script>
</body>

[main.js]

var app = new Vue({
  el: '#app',
  data: {
    list: [],
    current: '',
    topics: [
      { value: 'vue', name: 'Vue.js'},
      { value: 'jQuery', name: 'jQuery'}
    ]
  },
  watch: {
    current: function(val) {
      // GitHubのAPIからトピックのリポジトリを検索
      axios.get('https://api.github.com/search/repositories', {
        params: { q: 'topic:' + val }
      }).then(function(response) {
        this.list = response.data.items
      }.bind(this))
    }
  },
})