Як здійснити дебютацію у Vue2?


143

У мене є просте поле введення в шаблоні Vue, і я хотів би використовувати дебюнс більш-менш так:

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

Однак debounceмайно застаріло у Vue 2 . Рекомендація говорить лише: "використовувати v-on: input + стороння функція дебютації".

Як ви правильно це реалізуєте?

Я намагався реалізувати це за допомогою 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реквізиті.


1
Спробуйте один stackoverflow.com/questions/41230343 / ...
— sobolevn

3
Я б запропонував уважно прочитати: vuejs.org/v2/guide/…
— Marek

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 (редактор
— відмітки

5
Пропоноване рішення має проблему, коли на сторінці є декілька компонентних екземплярів. Проблема описується і рішення, представлене тут: forum.vuejs.org/t/isissue-with-vuejs-component-and-debounce/7224/…
— Валера,

e.currentTarget замінено на нуль таким чином
— ness-EE

1
Рекомендую додати a v-model=your_input_variableна вхід та у свій внесок data. Тож ви не покладаєтесь, e.targetа використовуєте 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
Див. Приклад посилань, схильних до гниття посилань. Краще поясніть проблему у відповіді - це зробить її більш цінною для читачів.
— MartinTeeVarga

Дякую, дуже співпадаю, я погано намагався зрозуміти, чому дані, що відображаються на консолі, були правильними, але не застосовані у програмі ...

@ sm4, оскільки замість того, щоб використовувати один і той самий спільний екземпляр, який дебютував для потрібної функції, він щоразу відтворює його, тим самим вбиваючи в основному використання debounce.
— Майк Шевард

1
просто додайте його до свого data()тоді.
— Су-Ау Хван

45

оновлено у 2020 році

Варіант 1: Повторне використання, без деп

(Рекомендується, якщо потрібно більше одного разу у вашому проекті)

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: Вбудований компонент, без пропусків

(Рекомендується, якщо використовується один раз або в невеликому проекті)

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
Я вважаю за краще цей варіант, тому що мені, мабуть, не потрібен пакет npm для 11 рядків коду ....
— Бен

3
Це має бути помітна відповідь. Це працює дуже добре і зовсім не займає місця. Дякую!
— Олександр Клудт

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,

Це нормально, якщо у вас є один або дуже мало випадків, коли вам потрібно зняти внесок. Однак ви швидко зрозумієте, що вам потрібно перенести це в бібліотеку або подібне, якщо додаток зростає, а ця функціональність потрібна в іншому місці. Зберігайте свій код ДУХОМ.
— Коріус

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()двічі за кожну зміну? v-on:виявить зміни входу та дебютування виклику, А оскільки модель прив’язана, функція перегляду searchInput ТАКОЖ дзвонить debounceInput... правда?
— mix3d

@ mix3d Не враховуйте цю відповідь. Я просто не хотів ставити питання про своє розслідування. Ви, швидше за все, праві. Перевірте прийняту відповідь. Це правильно, і я відредагував це, щоб відповідати питанню.
— MartinTeeVarga

Моя помилка ... Я не розумів, що ти відповів на власне запитання, га!
— mix3d

5

Якщо вам потрібен дуже мінімалістичний підхід до цього, я зробив такий (спочатку розщеплений від 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>

Ймовірно, це має бути прийнятим рішенням із 100+ голосами. ОП попросило такого компактного рішення, і воно чудово розв'язує логіку дебютації.
— Барні

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-моделі, підтримує користувацькі компоненти та елементи 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.lazyзамість цього, debounceале пам'ятайте, що v.model.lazyвін не завжди працює, оскільки Vue обмежує його для користувацьких компонентів.

Для користувацьких компонентів ви повинні використовувати :valueразом із@change.native

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


0

Якщо ви можете перемістити виконання функції debounce в якийсь метод класу, ви можете використовувати декоратор з lib utils-decorators ( 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 елемент може одночасно використовувати його. Це страшна відповідь.
— Гібридна веб-розробка

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

vue-властивість-декоратор


2
Не могли б ви додати більше інформації про це рішення?
— rocha

2
Будь ласка, детальніше докладніше. Також зауважте, що це стара нитка з чітко сформованими відповідями, тож чи можете ви уточнити, як ваше рішення більше відповідає проблемі?
— jpnadas

Це допомагає більше, якщо ви надасте пояснення, чому це найкраще рішення та поясніть, як воно працює. Ми хочемо вчити, а не просто надавати код.
— Олов'яний чоловік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.