Bagaimana cara menerapkan debounce di Vue2?


143

Saya memiliki kotak input sederhana di template Vue dan saya ingin menggunakan debounce lebih atau kurang seperti ini:

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

Namun debounceproperti telah ditinggalkan dalam Vue 2 . Rekomendasi hanya mengatakan: "gunakan v-on: input + fungsi debounce pihak ketiga".

Bagaimana Anda menerapkannya dengan benar?

Saya sudah mencoba mengimplementasikannya menggunakan lodash , v-on: input dan v-model , tetapi saya bertanya-tanya apakah mungkin dilakukan tanpa variabel tambahan.

Dalam template:

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

Dalam skrip:

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

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

Filterkey kemudian digunakan kemudian dalam computedalat peraga.



3
Saya sarankan untuk membaca dengan cermat: vuejs.org/v2/guide/…
Marek Urbanowicz

3
Ada contoh dalam panduan ini: vuejs.org/v2/guide/computed.html#Watchers
Bengt

Jawaban:


158

Saya menggunakan paket NPM debounce dan diimplementasikan seperti ini:

<input @input="debounceInput">

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

Menggunakan lodash dan contoh dalam pertanyaan, implementasinya terlihat seperti ini:

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

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

10
Terima kasih untuk ini. Saya menemukan contoh serupa di beberapa dokumen Vue lainnya: vuejs.org/v2/examples/index.html (editor penurunan harga)
MartinTeeVarga

5
Solusi yang diajukan memiliki masalah ketika ada beberapa komponen contoh pada halaman. Masalah dijelaskan dan solusi disajikan di sini: forum.vuejs.org/t/issues-with-vuejs-component-and-debounce/7224/…
Valera

e.currentTarget ditimpa menjadi nol dengan cara ini
ness-EE

1
Akan merekomendasikan untuk menambahkan v-model=your_input_variableinput dan di vue Anda data. Jadi Anda tidak bergantung pada e.targettetapi gunakan Vue sehingga Anda dapat mengakses this.your_input_variablealih-alihe.target.value
DominikAngerer

1
Bagi mereka yang menggunakan ES6, penting untuk menekankan penggunaan fungsi anonim di sini: jika Anda menggunakan fungsi panah, Anda tidak akan dapat mengakses thisdalam fungsi.
Polosson

68

Menugaskan debounce methodsbisa menjadi masalah. Jadi alih-alih ini:

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

Anda dapat mencoba:

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

Ini menjadi masalah jika Anda memiliki beberapa instance komponen - mirip dengan cara dataseharusnya fungsi yang mengembalikan objek. Setiap instance membutuhkan fungsi debounce sendiri jika mereka harus bertindak secara independen.

Berikut ini contoh masalahnya:

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
Bisakah Anda menjelaskan mengapa menetapkan debounce dalam metode bisa menjadi masalah?
MartinTeeVarga

12
Lihat Contoh tautan rentan terhadap pembusukan tautan. Lebih baik menjelaskan masalah dalam jawaban - itu akan membuatnya lebih berharga bagi pembaca.
MartinTeeVarga

Terima kasih sangat cocok, saya memiliki waktu yang buruk mencoba untuk memahami mengapa data yang ditampilkan pada konsol itu benar tetapi tidak diterapkan pada aplikasi ...

@ sm4 karena alih-alih menggunakan instance terbagi yang sama untuk fungsi yang Anda inginkan, itu menciptakan kembali setiap kali, sehingga membunuh penggunaan debounce terutama.
Mike Sheward

1
cukup tambahkan ke Anda data()kemudian.
Su-Au Hwang

45

diperbarui pada tahun 2020

Opsi 1: Dapat digunakan kembali, tanpa deps

(Disarankan jika diperlukan lebih dari sekali dalam proyek Anda)

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


Opsi 2: Dalam komponen, tanpa deps

(Disarankan jika menggunakan sekali atau dalam proyek kecil)

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
you the real hero
Ashtonian

4
Saya lebih suka opsi ini karena saya mungkin tidak memerlukan paket npm untuk 11 baris kode ....
Ben Winding

3
Ini harus menjadi jawaban yang ditandai, ini bekerja dengan sangat baik dan hampir tidak membutuhkan ruang sama sekali. Terima kasih!
Alexander Kludt

29

Sangat sederhana tanpa lodash

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

4
Seperti halnya saya suka lodash, ini jelas jawaban terbaik untuk debounce tambahan. Mudah diimplementasikan dan dipahami.
Michael Hays

2
juga merupakan hal yang baik untuk ditambahkan destroyed() { clearInterval(this.timeout) }agar tidak memiliki batas waktu setelah dihancurkan.
pikilon

13

Saya memiliki masalah yang sama dan ini adalah solusi yang berfungsi tanpa plugin.

Karena <input v-model="xxxx">persis sama dengan

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

(sumber)

Saya pikir saya bisa mengatur fungsi debounce pada penugasan xxxx di xxxx = $event.target.value

seperti ini

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

metode:

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

1
jika bidang input Anda juga memiliki @input="update_something"tindakan, panggil ini setelahthat.xxx = val that.update_something();
Neon22

1
di bagian metode saya, saya menggunakan sintaks yang sedikit berbeda yang bekerja untuk saya: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

Ini ok jika Anda memiliki satu atau sangat sedikit contoh di mana Anda perlu mendebo input. Namun, Anda akan segera menyadari bahwa Anda harus memindahkan ini ke perpustakaan atau serupa jika aplikasi tumbuh dan fungsi ini diperlukan di tempat lain. Simpan kode Anda KERING.
Coreus

5

Harap dicatat bahwa saya memposting jawaban ini sebelum jawaban yang diterima. Itu tidak benar. Itu hanya satu langkah maju dari solusi dalam pertanyaan. Saya telah mengedit pertanyaan yang diterima untuk menunjukkan implementasi penulis dan implementasi akhir yang saya gunakan.


Berdasarkan komentar dan dokumen migrasi yang ditautkan , saya telah membuat beberapa perubahan pada kode:

Dalam template:

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

Dalam skrip:

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

Dan metode yang menetapkan kunci filter tetap sama:

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

Sepertinya ada satu panggilan kurang (hanya v-model, dan bukan v-on:input).


Tidakkah panggilan ini debounceInput()dua kali untuk setiap perubahan? v-on:akan mendeteksi perubahan input dan memanggil debounce, DAN karena modelnya terikat, fungsi arloji searchInput juga akan memanggil debounceInput... benar?
mix3d

@ mix3d Jangan pertimbangkan jawaban ini. Itu hanya penyelidikan saya, saya tidak ingin mengajukan pertanyaan. Anda kemungkinan besar benar. Periksa jawaban yang diterima. Itu benar dan saya mengeditnya untuk mencocokkan pertanyaan.
MartinTeeVarga

Kesalahan saya ... Saya tidak sadar Anda telah menjawab pertanyaan Anda sendiri, ha!
mix3d

5

Jika Anda memerlukan pendekatan yang sangat minimalis untuk ini, saya membuat satu (awalnya bercabang dari vuejs-tips untuk juga mendukung IE) yang tersedia di sini: https://www.npmjs.com/package/v-debounce

Pemakaian:

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

Kemudian di komponen Anda:

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

Mungkin yang ini harus menjadi solusi yang diterima, dengan 100+ suara. OP meminta solusi ringkas seperti ini, dan itu dengan baik memisahkan logika debounce.
Barney

1

Jika Anda perlu menerapkan penundaan dinamis dengan fungsi 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) {
    // ...
  }
}

Dan templat:

<template>
  //...

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

  //...
</template>

CATATAN: pada contoh di atas saya membuat contoh input pencarian yang dapat memanggil API dengan penundaan khusus yang disediakan diprops


1

Meskipun hampir semua jawaban di sini sudah benar, jika ada yang mencari solusi cepat saya punya petunjuk untuk ini. https://www.npmjs.com/package/vue-lazy-input

Ini berlaku untuk @input dan v-model, mendukung komponen khusus dan elemen DOM, debounce dan throttle.

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

Jika Anda menggunakan Vue, Anda juga bisa menggunakan v.model.lazyalih-alih debouncemengingatv.model.lazy tidak akan selalu berfungsi karena Vue membatasinya untuk komponen khusus.

Untuk komponen khusus yang harus Anda gunakan :valuebersama@change.native

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


0

Jika Anda bisa memindahkan eksekusi fungsi debounce ke beberapa metode kelas Anda bisa menggunakan dekorator dari utils-decorators lib ( npm install --save utils-decorators):

import {debounce} from 'utils-decorators';

class SomeService {

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

-1

Kita dapat melakukannya dengan menggunakan beberapa baris kode JS:

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

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

Solusi sederhana! Bekerja Sempurna! Semoga bermanfaat buat kalian.


2
Tentu ... jika Anda ingin mencemari ruang global dan membuatnya jadi hanya 1 elemen yang dapat menggunakannya sekaligus. Ini jawaban yang mengerikan.
Web dev hibrid

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

vue-properti-dekorator


2
Bisakah Anda menambahkan informasi lebih lanjut tentang solusi ini?
rocha

2
Tolong jelaskan sedikit lebih banyak. Juga, perhatikan bahwa ini adalah utas lama dengan jawaban yang sudah mapan, jadi bisakah Anda mengklarifikasi bagaimana solusi Anda lebih tepat untuk masalah tersebut?
jpnadas

Ini lebih membantu jika Anda memberikan penjelasan mengapa ini adalah solusi yang disukai dan menjelaskan cara kerjanya. Kami ingin mendidik, bukan hanya memberikan kode.
Manusia Timah
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.