Vue2でデバウンスを実装する方法は?


143

Vueテンプレートに単純な入力ボックスがあり、多かれ少なかれこのようにデバウンスを使用したいと思います。

<input type="text" v-model="filterKey" debounce="500">

ただし、このdebounceプロパティはVue 2では廃止れました。推奨事項には、「v-on:input +サードパーティのデバウンス機能を使用する」とのみ記載されています。

どのように正しく実装しますか?

lodashv-on:inputv-modelを使用して実装しようとしましたが、追加の変数なしで実行できるかどうか疑問に思っています。

テンプレート:

<input type="text" v-on:input="debounceInput" v-model="searchInput">

スクリプトで:

data: function () {
  return {
    searchInput: '',
    filterKey: ''
  }
},

methods: {
  debounceInput: _.debounce(function () {
    this.filterKey = this.searchInput;
  }, 500)
}

フィルターキーは、後でcomputed小道具で使用されます。



3
私は慎重に読むことをお勧めします:vuejs.org/v2/guide/...
マレクUrbanowicz

3
ガイドに例があります:vuejs.org/v2/guide/computed.html#Watchers
Bengt

回答:


158

私はデバウンス NPMパッケージを使用しており、次のように実装されています。

<input @input="debounceInput">

methods: {
    debounceInput: debounce(function (e) {
      this.$store.dispatch('updateInput', e.target.value)
    }, config.debouncers.default)
}

lodashと問題の例を使用すると、実装は次のようになります。

<input v-on:input="debounceInput">

methods: {
  debounceInput: _.debounce(function (e) {
    this.filterKey = e.target.value;
  }, 500)
}

10
これをありがとう。他のVueドキュメントで同様の例を見つけました:vuejs.org/v2/examples/index.htmlマークダウンエディター)
MartinTeeVarga 2017

5
ページに複数のコンポーネントインスタンスがある場合、提案されたソリューションには問題があります。問題点を説明し、解決策はここに提示されていますforum.vuejs.org/t/issues-with-vuejs-component-and-debounce/7224/...
バレラ

e.currentTargetはこの方法でnullに上書きされます
ness-EE

1
v-model=your_input_variable入力とVueにaを追加することをお勧めしますdata。そのe.targetため、Vueを使用せずにVueを使用して、this.your_input_variable代わりにアクセスできますe.target.value
DominikAngerer

1
ES6を使用している場合は、ここで無名関数の使用を強調することが重要です。矢印関数を使用するとthis、関数内でアクセスできなくなります。
ポロソン

68

デバウンスの割り当てはmethods問題になる可能性があります。したがって、これの代わりに:

// Bad
methods: {
  foo: _.debounce(function(){}, 1000)
}

あなたは試すことができます:

// Good
created () {
  this.foo = _.debounce(function(){}, 1000);
}

コンポーネントのインスタンスが複数ある場合は問題になります- dataオブジェクトを返す関数である必要があるのと同様の方法です。各インスタンスは、独立して動作することが想定されている場合、独自のデバウンス機能を必要とします。

これは問題の例です:

Vue.component('counter', {
  template: '<div>{{ i }}</div>',
  data: function(){
    return { i: 0 };
  },
  methods: {
    // DON'T DO THIS
    increment: _.debounce(function(){
      this.i += 1;
    }, 1000)
  }
});


new Vue({
  el: '#app',
  mounted () {
    this.$refs.counter1.increment();
    this.$refs.counter2.increment();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>

<div id="app">
  <div>Both should change from 0 to 1:</div>
  <counter ref="counter1"></counter>
  <counter ref="counter2"></counter>
</div>


1
メソッドでデバウンスを割り当てることが問題になる理由を説明できますか?
MartinTeeVarga

12
リンクの例はlink-rotになりやすいを参照してください。答えで問題を説明する方が良いです-それは読者にとってそれをより価値のあるものにします。
MartinTeeVarga

どうもありがとうございます。コンソールに表示されたデータが正しいのに、アプリに適用されなかった理由を理解するのに

@ sm4は、目的の関数に同じ共有デバウンスインスタンスを使用する代わりに、毎回それを再作成するため、主にデバウンスの使用を中止します。
Mike Sheward

1
それをあなたのdata()thenに追加するだけです。
Su-Au Hwang

44

2020年に更新

オプション1:再利用可能、depなし

(プロジェクトで複数回必要な場合に推奨)

helpers.js

export function debounce (fn, delay) {
  var timeoutID = null
  return function () {
    clearTimeout(timeoutID)
    var args = arguments
    var that = this
    timeoutID = setTimeout(function () {
      fn.apply(that, args)
    }, delay)
  }
}

Component.vue

<script>
  import {debounce} from './helpers'

  export default {
    data () {
      return {
        input: '',
        debouncedInput: ''
      }
    },
    watch: {
      input: debounce(function (newVal) {
        this.debouncedInput = newVal
      }, 500)
    }
  }
</script>

Codepen


オプション2:インコンポーネント、depなし

(1回または小規模なプロジェクトで使用する場合に推奨)

Component.vue

<template>
    <input type="text" v-model="input" />
</template>

<script>
  export default {
    data: {
      debouncedInput: ''
    },
    computed: {
     input: {
        get() {
          return this.debouncedInput
        },
        set(val) {
          if (this.timeout) clearTimeout(this.timeout)
          this.timeout = setTimeout(() => {
            this.debouncedInput = val
          }, 300)
        }
      }
    }
  }
</script>

Codepen


4
あなたは本当のヒーローです
アシュトニアン

4
私はおそらく11行のコードにnpmパッケージを必要としないため、このオプションを選択します...
Ben Winding

3
これはマークされた答えになるはずです。これは非常にうまく機能し、スペースをほとんど取りません。ありがとう!
Alexander Kludt

29

ロダッシュなしの非常にシンプルな

  handleScroll: function() {
   if (this.timeout) clearTimeout(this.timeout); 
   this.timeout = setTimeout(() => {
     // your action
   }, 200);
  }

4
私がロダッシュを愛しているのと同じくらい、これは明らかにトレーリングデバウンスの最良の答えです。実装と理解が最も簡単です。
マイケルヘイズ

2
destroyed() { clearInterval(this.timeout) }破棄後にタイムアウトにならないようにするために追加するのも良いことです。
ピキロン

13

私は同じ問題を抱えていましたが、ここにプラグインなしで動作する解決策があります。

以来<input v-model="xxxx">と全く同じです

<input
   v-bind:value="xxxx"
   v-on:input="xxxx = $event.target.value"
>

(ソース)

私はxxxxの割り当てにデバウンス機能を設定できると考えました xxxx = $event.target.value

このような

<input
   v-bind:value="xxxx"
   v-on:input="debounceSearch($event.target.value)"
>

メソッド:

debounceSearch(val){
  if(search_timeout) clearTimeout(search_timeout);
  var that=this;
  search_timeout = setTimeout(function() {
    that.xxxx = val; 
  }, 400);
},

1
入力フィールドにも@input="update_something"アクションがあった場合は、この後に呼び出しますthat.xxx = val that.update_something();
Neon22

1
私の方法の項に、私は私のために働いたわずかに異なる構文を使用:debounceSearch: function(val) { if (this.search_timeout) clearTimeout(this.search_timeout); var that=this; this.search_timeout = setTimeout(function() { that.thread_count = val; that.update_something(); }, 500); },
Neon22

入力をデバウンスする必要があるインスタンスが1つまたは非常に少ない場合、これは問題ありません。ただし、アプリが大きくなり、この機能が他の場所で必要になった場合は、これをライブラリなどに移動する必要があることにすぐに気付くでしょう。コードは乾かしてください。
Coreus

5

承認された回答の前にこの回答を投稿したことに注意してください。不正解です。これは、問題の解決策からの前進にすぎません。承認された質問を編集して、著者の実装と使用した最終的な実装の両方を表示しました。


コメントとリンクされた移行ドキュメントに基づいて、コードにいくつかの変更を加えました。

テンプレート:

<input type="text" v-on:input="debounceInput" v-model="searchInput">

スクリプトで:

watch: {
  searchInput: function () {
    this.debounceInput();
  }
},

フィルターキーを設定するメソッドは同じままです。

methods: {
  debounceInput: _.debounce(function () {
    this.filterKey = this.searchInput;
  }, 500)
}

このルックス一つ少なくコール(ただそこにあるようにv-model、そしてませんv-on:input)。


これdebounceInput()は、変更ごとに2回呼び出されませんか?v-on:入力の変化を検出してデバウンスを呼び出し、モデルがバインドされているため、searchInputの監視関数もdebounceInput...を呼び出しますよね?
mix3d 2017年

@ mix3dこの回答は考慮しないでください。質問したくない私の調査でした。あなたはおそらく正しいです。受け入れられた回答を確認してください。正解です。質問に合わせて編集しました。
MartinTeeVarga

私の間違い...あなたがあなた自身の質問に答えたのに気づかなかった、ハ!
mix3d 2017年

5

これに非常に最小限のアプローチが必要な場合は、https://www.npmjs.com/package/v-debounceから入手できる方法(もともとvuejs-tipsからフォークしてIEもサポートする)を作成しました

使用法:

<input v-model.lazy="term" v-debounce="delay" placeholder="Search for something" />

次に、コンポーネントで:

<script>
export default {
  name: 'example',
  data () {
    return {
      delay: 1000,
      term: '',
    }
  },
  watch: {
    term () {
      // Do something with search term after it debounced
      console.log(`Search term changed to ${this.term}`)
    }
  },
  directives: {
    debounce
  }
}
</script>

たぶん、これは100以上の賛成投票で受け入れられる解決策になるはずです。OPは、このようなコンパクトなソリューションを求め、デバウンスロジックをうまく切り離しました。
Barney

1

lodashのdebounce関数で動的遅延を適用する必要がある場合:

props: {
  delay: String
},

data: () => ({
  search: null
}),

created () {
     this.valueChanged = debounce(function (event) {
      // Here you have access to `this`
      this.makeAPIrequest(event.target.value)
    }.bind(this), this.delay)

},

methods: {
  makeAPIrequest (newVal) {
    // ...
  }
}

そしてテンプレート:

<template>
  //...

   <input type="text" v-model="search" @input="valueChanged" />

  //...
</template>

注:上記の例では、提供されているカスタム遅延でAPIを呼び出すことができる検索入力の例を作成しましたprops


1

ここでの回答のほとんどはすでに正しいですが、誰かが簡単な解決策を探している場合、私はこれに関する指示を持っています。 https://www.npmjs.com/package/vue-lazy-input

@inputとv-modelに適用され、カスタムコンポーネントとDOM要素、デバウンスとスロットルをサポートします。

Vue.use(VueLazyInput)
  new Vue({
    el: '#app', 
    data() {
      return {
        val: 42
      }
    },
    methods:{
      onLazyInput(e){
        console.log(e.target.value)
      }
    }
  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/lodash/lodash.min.js"></script><!-- dependency -->
<script src="https://unpkg.com/vue-lazy-input@latest"></script> 

<div id="app">
  <input type="range" v-model="val" @input="onLazyInput" v-lazy-input /> {{val}}
</div>


0

Vueを使用している場合v.model.lazydebounce、代わりに使用することもできますが、v.model.lazyカスタムコンポーネントに対して制限するため、常に機能するとは限りません。

:value一緒に使用する必要があるカスタムコンポーネント@change.native

<b-input :value="data" @change.native="data = $event.target.value" ></b-input>


0

デバウンス関数の実行をいくつかのクラスメソッドに移動できる場合は、utils-decorators lib(npm install --save utils-decorators)のデコレーターを使用できます。

import {debounce} from 'utils-decorators';

class SomeService {

  @debounce(500)
  getData(params) {
  }
}

-1

数行のJSコードを使用することで対処できます。

if(typeof window.LIT !== 'undefined') {
      clearTimeout(window.LIT);
}

window.LIT = setTimeout(() => this.updateTable(), 1000);

シンプルなソリューション!パーフェクトワーク!皆さんに役立つことを願っています。


2
確かに...グローバルスペースを汚染して、一度に1つの要素のみが使用できるようにする場合は、これはひどい答えです。
ハイブリッドWeb開発者

-1
 public debChannel = debounce((key) => this.remoteMethodChannelName(key), 200)

vue-property-decorator


2
このソリューションに関する詳細情報を追加していただけませんか?
ロチャ

2
もう少し詳しく説明してください。また、これは十分に確立された回答を持つ古いスレッドであることにも注意してください。そのため、ソリューションが問題に対してどのように適切であるかを明確にできますか?
jpnadas

これが望ましいソリューションである理由と、それがどのように機能するかを説明すると、さらに役立ちます。コードを提供するだけでなく、教育したいと考えています。
ティンマン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.