Vue2 debounce nasıl uygulanır?


143

Bir Vue şablonunda basit bir giriş kutusu var ve ben debounce az ya da böyle kullanmak istiyorum:

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

Ancak debouncemülk Vue 2'de kullanımdan kaldırıldı . Öneri sadece şunu söylüyor: "v-on: input + 3rd party debounce function" kullanın.

Doğru şekilde nasıl uygularsınız?

Lodash , v-on: giriş ve v-model kullanarak uygulamaya çalıştım , ancak ekstra değişken olmadan yapmak mümkün olup olmadığını merak ediyorum.

Şablonda:

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

Komut dosyasında:

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

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

Daha sonra filtre tuşu computedsahne kısımlarında kullanılır .



3
Dikkatle okumanızı öneririm: vuejs.org/v2/guide/…
Marek Urbanowicz

Yanıtlar:


159

Ben debounce NPM paketi kullanıyorum ve şu şekilde uygulanır:

<input @input="debounceInput">

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

Lodash ve sorudaki örneği kullanarak , uygulama şöyle görünür:

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

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

10
Bunun için teşekkürler. Diğer bazı Vue belgelerinde de benzer bir örnek buldum: vuejs.org/v2/examples/index.html (etiketleme editörü)
MartinTeeVarga

5
Sayfada birkaç bileşen örneği olduğunda önerilen çözümde bir sorun vardır. Sorun burada açıklanıyor ve çözüm burada sunuluyor: forum.vuejs.org/t/issues-with-vuejs-component-and-debounce/7224/…
Valera

e.currentTarget, bu şekilde null değerine yazılır
ness-EE

1
v-model=your_input_variableGirdiye ve sıranıza bir eklemenizi tavsiye ederim data. Bu yüzden güvenmiyorsunuz, e.targetancak Vue kullanıyorsunuz, this.your_input_variablebunun yerine erişebiliyorsunuze.target.value
DominikAngerer

1
ES6 kullananlar için, burada anonim işlevin kullanımını vurgulamak önemlidir: bir ok işlevi kullanırsanız this, işlev içinde erişemezsiniz .
Polosson

68

Geri dönmeyi atamak methodssorun olabilir. Bunun yerine:

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

Deneyebilirsiniz:

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

Bir bileşenin birden çok örneğine sahipseniz sorun olur - benzer şekilde databir nesne döndüren bir işlev olmalıdır. Bağımsız olarak hareket etmeleri gerekiyorsa, her örneğin kendi geri dönme işlevine ihtiyacı vardır.

İşte sorunun bir örneği:

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
Yöntemlerde geri ödeme atamanın neden sorun olabileceğini açıklayabilir misiniz?
MartinTeeVarga

12
Bkz. Örnek bağlantılar link-rot'a eğilimlidir. Sorunu cevapta açıklamak daha iyidir - okuyucular için daha değerli hale getirecektir.
MartinTeeVarga

Çok maç teşekkür ederim, konsolda görüntülenen verilerin neden doğru olduğunu ancak uygulamada uygulanmadığını anlamaya çalışırken kötü bir zaman geçirdim ...

@ sm4, çünkü istediğiniz işlev için aynı paylaşılan debounced örneğini kullanmak yerine, her seferinde yeniden yaratır, böylece debounce kullanımını çoğunlukla öldürür.
Mike Sheward

1
sadece o data()zaman ekleyin .
Su-Au Hwang

46

2020'de güncellendi

Seçenek 1: Yeniden kullanılabilir, deps yok

(Projenizde birden fazla gerekiyorsa önerilir)

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


Seçenek 2: Bileşen içinde, gecikme yok

(Bir kez veya küçük projede kullanılıyorsa önerilir)

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
sen gerçek kahraman
Ashtonian

4
Bu seçeneği tercih ediyorum çünkü muhtemelen 11 satır kod için bir npm paketine ihtiyacım yok ....
Ben Winding

3
Bu işaretli cevap olmalı, bu gerçekten iyi çalışıyor ve neredeyse hiç yer kaplamaz. Teşekkürler!
Alexander Kludt

29

Lodash olmadan çok basit

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

4
Lodash'ı sevdiğim kadarıyla, bu sondaki bir geri dönüş için en iyi cevap. Uygulaması ve anlaması en kolay.
Michael Hays

2
ayrıca destroyed() { clearInterval(this.timeout) }yok edildikten sonra bir zaman aşımı olmaması için eklemek iyi bir şeydir .
pikilon

13

Aynı sorunu yaşadım ve burada eklenti olmadan çalışan bir çözüm var.

Çünkü <input v-model="xxxx">tam olarak aynı

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

(kaynak)

Ben xxxx tayininde bir debounce fonksiyonu ayarlayabilirsiniz düşündüm xxxx = $event.target.value

bunun gibi

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

yöntemleri:

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

1
giriş alanınız da bir @input="update_something"eylem that.xxx = val that.update_something();
yaptıysa

1
benim yöntemler bölümünde benim için çalışan biraz farklı bir sözdizimi kullandım: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

Bu, girdiyi geri almanız gereken bir veya çok az örneğiniz varsa tamamdır. Ancak, uygulama büyürse ve bu işlevsellik başka bir yerde gerekliyse bunu bir kütüphaneye veya benzerine taşımanız gerekeceğini hızlı bir şekilde fark edeceksiniz. Kodunuzu KURU tutun.
Coreus

5

Bu cevabı kabul edilen cevaptan önce gönderdiğimi lütfen unutmayın. Bu doğru değil. Söz konusu çözümden sadece bir adım ileri. Kabul edilen soruyu hem yazarın uygulamasını hem de kullandığım son uygulamayı göstermek için düzenledim.


Yorumlara ve bağlantılı taşıma belgesine dayanarak , kodda birkaç değişiklik yaptım:

Şablonda:

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

Komut dosyasında:

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

Ve filtre anahtarını ayarlayan yöntem aynı kalır:

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

Bu daha az bir çağrı var gibi görünüyor (sadece v-model, ve değil v-on:input).


Bu debounceInput(), her değişiklik için iki kez çağrılamaz mı? v-on:giriş değişikliklerini ve çağrı geri çağrıyı algılayacak ve model bağlı olduğu için searchInput'un izleme işlevi de ÇAĞRI debounceInput... doğru mu?
mix3d

@ mix3d Bu yanıtı dikkate almayın. Soruyu sormak istemediğim sadece araştırmamdı. Büyük olasılıkla haklısın. Kabul edilen cevabı kontrol edin. Doğru ve soruyu eşleştirmek için düzenledim.
MartinTeeVarga

Benim hatam ... Kendi sorunuzu cevapladığınızı bilmiyordum, ha!
mix3d

5

Buna çok minimalist bir yaklaşıma ihtiyacınız varsa, burada mevcut olan bir tane (aslında vuejs-tips'ten IE'yi desteklemek için çatallanmış) yaptım: https://www.npmjs.com/package/v-debounce

Kullanımı:

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

Sonra bileşeninizde:

<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>

Muhtemelen bu çözüm 100'den fazla oyla kabul edilen çözüm olmalıdır. OP böyle kompakt bir çözüm istedi ve ayrılma mantığını güzelce ayırıyor.
Barney

1

Lodash'ın debounceişleviyle dinamik bir gecikme uygulamanız gerekiyorsa :

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) {
    // ...
  }
}

Ve şablon:

<template>
  //...

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

  //...
</template>

NOT: yukarıdaki örnekte, API'da sağlanan özel bir gecikmeyle çağırabilecek arama girişi örneği yaptımprops


1

Burada hemen hemen tüm cevaplar zaten doğru olmasına rağmen, kimse hızlı bir çözüm arayışı içinde ise bunun için bir direktifim var. https://www.npmjs.com/package/vue-lazy-input

@İnput ve v-model için geçerlidir, özel bileşenleri ve DOM öğelerini destekler, açılma ve gaz kelebeği.

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 kullanıyorsanız, v.model.lazybunun yerine kullanabilirsiniz debounceancak unutmayın v.model.lazy, Vue her zaman özel bileşenler için sınırlandırdığından çalışmaz.

Özel bileşenler için :valuebirlikte kullanmalısınız@change.native

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


0

Debounce işlevinin yürütülmesini bir sınıf yöntemine taşıyabilirseniz, utils-decorators lib ( npm install --save utils-decorators) ' den bir dekoratör kullanabilirsiniz :

import {debounce} from 'utils-decorators';

class SomeService {

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

-1

Birkaç JS kodu satırı kullanarak yapabiliriz:

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

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

Basit bir çözüm! Mükemmel Çalış! Umarım sizin için yararlı olacaktır.


2
Elbette ... eğer küresel alanı kirletmek ve onu bir seferde sadece 1 elementin kullanabilmesini istiyorsanız. Bu korkunç bir cevap.
Hibrit web dev

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

vue-mülk-dekoratör


2
Bu çözüm hakkında daha fazla bilgi ekleyebilir misiniz?
rocha

2
Lütfen biraz daha ayrıntı verin. Ayrıca, bunun iyi yapılandırılmış cevapları olan eski bir konu olduğunu unutmayın, bu nedenle çözümünüzün sorun için nasıl daha uygun olduğunu açıklayabilir misiniz?
jpnadas

Bunun neden tercih edilen çözüm olduğunu açıklar ve nasıl çalıştığını açıklarsanız daha fazla yardımcı olur. Sadece kod sağlamakla kalmayıp eğitmek istiyoruz.
Tin Man
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.