Подтвердить что ты не робот

Как реализовать debounce в Vue2?

У меня есть простое поле ввода в шаблоне Vue, и я хотел бы использовать debounce более или менее следующим образом:

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

Однако свойство debounce устарело в Vue 2. В рекомендации говорится: "использовать v-on: вход + функция дебаза третьей стороны".

Как вы его правильно реализуете?

Я попытался реализовать его с помощью lodash, v-on: input и v-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 реквизитах.

4b9b3361

Ответ 1

Я использую debounce пакет 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)
}

Ответ 2

Назначение debounce в methods может быть проблемой. Итак, вместо этого:

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

Вы можете попробовать:

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

Это становится проблемой, если у вас есть несколько экземпляров компонента - подобно тому, как data должны быть функцией, которая возвращает объект. Каждому экземпляру нужна своя функция debounce, если предполагается, что они действуют независимо.

Вот пример проблемы:

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>

Ответ 3

У меня была такая же проблема, и это работало без плагинов.

Поскольку <input v-model="xxxx"> точно такой же, как

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

(источник)

Я подумал, что мог бы установить функцию debounce при назначении 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);
},

Ответ 4

Очень просто без лодаш

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

Ответ 5

Повторное использование и без deps:

helpers.js

module.exports = 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)
  }
}

.vue

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

(вдохновлено крошечным дебатом)

Ответ 6

Обратите внимание, что я отправил этот ответ перед принятым ответом. Это не правильно. Это всего лишь шаг вперед от решения в вопросе. Я отредактировал принятый вопрос, чтобы показать как реализацию автора, так и конечную реализацию, которую я использовал.


Основываясь на комментариях и связанном документе миграции, я внес несколько изменений в код:

В шаблоне:

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

Ответ 7

Если вам нужен минималистичный подход к этому, я сделал один (изначально разработанный для vuejs-tips для поддержки IE), который доступен здесь: https://www.npmjs.com/package/v-debounce

Применение:

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

Ответ 8

Если вы используете Vue, вы также можете использовать v.model.lazy вместо debounce но помните, что v.model.lazy не всегда будет работать, поскольку Vue ограничивает его для пользовательских компонентов.

Для пользовательских компонентов вы должны использовать :value вместе с @change.native

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

Ответ 9

Мы можем сделать с помощью нескольких строк кода JS:

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

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

Простое решение! Идеальная работа! Надеюсь, будет полезно для вас, ребята.

Ответ 10

В случае, если необходимо применить динамическую задержку с 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

Ответ 11

Хотя почти все ответы здесь уже верны, если кто-то ищет быстрое решение, у меня есть для этого директива. https://www.npmjs.com/package/vue-lazy-input

Это относится к @input и v-модели, поддерживает пользовательские компоненты и элементы DOM, debounce и 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/[email protected]"></script> 

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