Vuejs ポーリングする時の注意点

setIntervalとは

一定の遅延間隔を置いて関数を繰り返し実行したいときに利用する関数。 ポーリングで利用することが多い。 参考 setInterval

1秒ごとにコンソール出力する例。

<!doctype html>
<html lang="ja">
  <script>
    window.setInterval(function(){
      console.log("polling");
    }, 1000);
  </script>
  <body>
  </body>
</html>

setIntervalの生存期間

リファレンスに記載がないので、動作確認したところ以下のようになった。
参考 www.w3.org
参考 developer.mozilla.org

  • DOMをunloadして次のDOMをloadするまで(=Documentオブジェクトと同じ生存期間)
  • ページ内リンクやJSでの動的なDOM書き換え、はsetIntervalがクリアされない
  • ページ外リンク、はsetIntervalがクリアされる

WindowオブジェクトとDocumentオブジェクトの関係は以下を参考に。
参考 Window オブジェクトや Document オブジェクト、DOMなど

setIntervalの動作確認

  • Google Chrome 60.0.3112.90
<!doctype html>
<html lang="ja">
<script>
  window.onload = function () {
    window.setInterval(function() { console.log("hi"); }, 1000);
    document.getElementById('btn').addEventListener('click', function() {
        var parent = document.getElementsByTagName('body')[0];
        while(parent.firstChild) parent.removeChild(parent.firstChild);
        var newElm = document.createElement('div');
        newElm.textContent = 'hello world';
        parent.appendChild(newElm);
    });
  }
</script>
  <body>
    <a href="#xxx">ページ内リンク</a>
    <a href="other.html">ページ外リンク</a>
    <button id="btn" type="button">JSで動的に書き替える</button>
    <p>dummy</p>
    ...
    <p>dummy</p>
    <p id="xxx">xxx</p>
    <p>dummy</p>
    ...
    <p>dummy</p>
  </body>
</html>
<!doctype html>
<html lang="ja">
<body>
<a href="polling.html">戻る</a>
</body>
</html>

f:id:kimulla:20191201211355g:plain

Vue.jsでsetIntervalを使ってポーリングするときの注意点

SPAの場合、データのみリクエストしてDOMを差分更新することになる。そのため、Documentオブジェクトの入れ替えが起こらず、一度setIntervalしたタイマは、全画面で有効になり続ける

検証

ポーリングしたいページ

<template>
  <div>
    <h1>polling</h1>
   <router-link to="/goodbye">Goodbye</router-link>
  </div>
</template>

<script>
export default {
  name: 'hello',
  mounted () {
    setInterval(function () {
      console.log('hi')
    }, 1000)
  }
}
</script>

ポーリングしたくないページ

<template>
  <div>
    <h1>ポーリングしたくないページ</h1>
  </div>
</template>

<script>
export default {
  name: 'boodbye'
}
</script>

f:id:kimulla:20191201211453g:plain

問題点

ポーリングによる、無駄なネットワークコストがかかる。

解決方法

clearIntervalを利用して、タイマを削除する。
参考 (clearInterval)https://developer.mozilla.org/ja/docs/Web/API/WindowTimers/clearInterval

ページを遷移する際にclearIntervalを呼ぶためには、ライフサイクルフックを利用する。
参考 ライフサイクルフック

<template>
  <div>
    <h1>polling</h1>
   <router-link to="/goodbye">Goodbye</router-link>
  </div>
</template>

<script>
export default {
  name: 'hello',
  data: function () {
    return {
      intervalId: undefined
    }
  },
  mounted () {
    this.intervalId = setInterval(function () {
      console.log('hi')
    }, 1000)
  },
  beforeDestroy () {
    console.log('clearInterval')
    clearInterval(this.intervalId)
  }
}
</script>

f:id:kimulla:20191201211526g:plain

結論

Vue.js(というかSPA全般)でsetIntervalするときは、clearIntervalしよう。