Vue2에서 디 바운스를 구현하는 방법은 무엇입니까?


143

Vue 템플릿에 간단한 입력 상자가 있으며 다음과 같이 디 바운스를 사용하고 싶습니다.

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

그러나이 debounce속성은 Vue 2에서 더 이상 사용되지 않습니다 . 권장 사항에는 "v-on : input + 타사 디 바운스 기능 사용"만 나와 있습니다.

어떻게 올바르게 구현합니까?

lodash , v-on : inputv-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소품 에서 사용됩니다 .



3
나는주의 깊게 읽어 제안 : vuejs.org/v2/guide/...
마렉 Urbanowicz

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 (마크 다운 편집기)
MartinTeeVarga

5
제안 된 솔루션에 페이지에 여러 구성 요소 인스턴스가있는 경우 문제가 있습니다. 다음은 문제에 대한 설명과 해결책입니다 : forum.vuejs.org/t/issues-with-vuejs-component-and-debounce/7224/…
Valera

e.currentTarget은이 방법으로 null로 덮어 쓰기
ness-EE

1
v-model=your_input_variable입력과 Vue에 a를 추가하는 것이 좋습니다 data. 따라서 의존하지 않고 e.targetVue를 사용하여 this.your_input_variable대신에 액세스 할 수 있습니다e.target.value
DominikAngerer

1
ES6를 사용하는 경우 익명 기능 사용을 강조하는 것이 중요합니다. 화살표 기능을 사용하면 기능 this내에서 액세스 할 수 없습니다 .
Polosson

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는 원하는 기능에 대해 동일한 공유 디 바운스 인스턴스를 사용하는 대신 매번 다시 생성하므로 디 바운스 사용을 주로 중단시킵니다.
Mike Sheward

1
당신의 data()다음에 추가하십시오 .
Su-Au Hwang

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>

코드 펜


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

코드 펜


4
당신은 진짜 영웅
Ashtonian

4
11 줄의 코드에 npm 패키지가 필요하지 않기 때문에이 옵션을 선호합니다 ..
Ben Winding

3
이것은 표시된 답변이어야합니다. 이것은 실제로 잘 작동하며 거의 공간을 차지하지 않습니다. 감사!
Alexander Kludt

29

lodash없이 매우 간단

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

4
내가 lodash를 좋아하는 한, 이것은 후행 디 바운스에 대한 가장 좋은 대답입니다. 구현하고 이해하기가 가장 쉽습니다.
Michael Hays

2
또한 destroyed() { clearInterval(this.timeout) }파괴 후 시간 초과가 발생하지 않도록 추가하는 것이 좋습니다 .
pikilon

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

입력을 디 바운스해야 할 인스턴스가 하나 이상인 경우에는 괜찮습니다. 그러나 앱이 커지고 다른 곳에서이 기능이 필요한 경우이를 라이브러리 또는 이와 유사한 것으로 이동해야한다는 것을 빨리 알게 될 것입니다. 코드를 건조하게 유지하십시오.
Coreus

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의 watch 함수도 호출합니다 debounceInput... 맞습니까?
mix3d

@ mix3d이 답변을 고려하지 마십시오. 내가 질문에 넣고 싶지 않은 것은 단지 내 조사였습니다. 당신이 가장 옳을 것입니다. 허용 된 답변을 확인하십시오. 정답이며 질문과 일치하도록 편집했습니다.
MartinTeeVarga

내 실수 ... 나는 네가 네 자신의 질문에 대답했다는 것을 몰랐다.
mix3d

5

이에 아주 최소한의 접근 방식이 필요하면, 내가 한 여기에서 확인할 수 있습니다 (원래 vuejs - 팁에서 포크도 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 개 이상의 투표로 인정되는 솔루션이어야합니다. OP는 이와 같은 소형 솔루션을 요구했으며 디 바운스 로직을 훌륭하게 분리했습니다.
바니

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-model에 적용되며 사용자 지정 구성 요소 및 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.lazyVue가 사용자 지정 구성 요소에 대해 Vue를 제한하기 때문에 항상 작동하지는 않습니다.

사용자 정의 구성 요소의 당신은 사용해야 :value과 함께@change.native

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


0

debounce 함수의 실행을 클래스 메소드로 옮길 수 있다면 utils-decorators lib ( 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
 public debChannel = debounce((key) => this.remoteMethodChannelName(key), 200)

Vue-property-decorator


2
이 솔루션에 대한 추가 정보를 추가해 주시겠습니까?
rocha

2
좀 더 정교하게 작성하십시오. 또한 이것은 잘 확립 된 답변을 가진 오래된 스레드이므로 솔루션이 문제에 어떻게 더 적합한 지 명확히 할 수 있습니까?
jpnadas

이것이 왜 선호되는 솔루션인지 설명하고 작동 방식을 설명하면 도움이됩니다. 우리는 단지 코드를 제공하는 것이 아니라 교육하기를 원합니다.
주석 맨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.