Як здійснити дебютацію у 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.