Vue.jsでいい感じに非同期の連投防止したい


Vue.jsで連投防止するのに、個人的に楽なやり方ができたのでメモ。連打しないでほしいね。

とりあえずブツ

以下のmixinを使う。

multipost_avoidable.js
export default {
  data() {
    return {
      isPosting: false
    };
  },
  methods: {
    beginPost() {
      this.isPosting = true;
    },
    endPost() {
      this.isPosting = false;
    },
    async avoidMultipost(func) {
      if (this.isPosting) {
        return null;
      }
      this.beginPost();
      let res = await func();
      this.endPost();
      return res;
    }
  }
};

つかいかた

上のmixinを import して連投が起こりうる処理を avoidMultipost のコールバックとして渡す。送信後に画面遷移する場合などは、その処理もコールバック内に記述しましょう。 async function であることに注意。

Component.vue
// いろいろ省略
import MultipostAvoidable from "/path/to/mixin/multipost_avoidable";

export default {
  mixins: [MultipostAvoidable],
  methods: {
    async post() {
      this.avoidMultipost(async () => {
        // ここに非同期の通信処理 ex.: await axios.post(/path, data)
      });
    }
  }
};

しくみ

単純ですが、data プロパティに isPosting というパラメータを持っておき avoidMultipost 内のコールバックの実行前後で、 isPosting の状態を切り替えています。 isPostingtrue の場合はコールバックを実行しないようにして連投を防止しています。

コンポーネント内で連投防止処理を書くときに毎回 this.beginPost(); this.endPost(); で囲むのがしんどかったり、書き忘れるのが怖いので、デコレータみたいな感じにした。

投稿と同時に Button 要素を無効にしたいとかいう場合は該当要素に :disabled="isPosting" とかしてあげればいいと思います。