如何在Vue2中实现反跳功能?


143

我在Vue模板中有一个简单的输入框,我想像这样或多或少使用去抖动:

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

然而,该debounce物业已在Vue 2中弃用。该建议仅说:“使用v-on:input +第三方防抖功能”。

您如何正确实施它?

我尝试使用lodashv-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道具中使用filterkey 。



3
我建议仔细阅读:vuejs.org/v2/guide/...
马立克Urbanowicz

3
该指南中有一个示例:vuejs.org/v2/guide/computed.html#Watchers
Bengt

Answers:


158

我正在使用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)
}

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
建议将a添加v-model=your_input_variable到输入中并在vue中data。因此,您无需依赖e.target而是使用Vue,因此可以访问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 '18

12
请参阅示例链接容易发生链接腐烂。最好在答案中说明问题-这样对读者来说将更有价值。
MartinTeeVarga '18

谢谢您的配合,我度过了一段

@ sm4,因为它没有为所需的功能使用相同的共享防抖动实例,而是每次都重新创建它,从而主要终止了使用防抖动功能。
Mike Sheward

1
只需将其添加到您的data()
Su-Au Hwang,

44

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
您是真正的英雄
阿什顿主义者

4
我喜欢这个选项,因为我可能不需要的NPM包的代码11行....
奔缠绕

3
这应该是标记出来的答案,这确实很好,几乎不占用任何空间。谢谢!
亚历山大·克鲁德

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

我的错...我没意识到你已经回答了自己的问题,哈!
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多个投票。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模型,支持自定义组件和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函数的执行转移到某些类方法中,则可以使用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个元素。这是一个可怕的答案。
混合式网络开发人员

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

Vue属性装饰器


2
您能否添加有关此解决方案的更多信息?
rocha

2
请详细说明。另外,请注意,这是一个具有完善答案的旧线程,因此您能否阐明您的解决方案如何更适合该问题?
jpnadas

如果您提供解释说明为什么这是首选解决方案并说明其工作原理,那么它会有所帮助。我们要教育,而不仅仅是提供代码。
锡民
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.