我有一个Vue组件,它使用多个子组件.在这些子组件上,我有一个监视程序,监视数据变化并处理这些变化.我想为此实现debounce.

    watch: {
    data: {
      handler: function () {
        this.processData()
      },
      deep: true
    }
  },
  methods: {
    processData: debounce(function () {
      console.log(this.id)
    }, 250),

问题是,debounce可以工作,因此它只在最后一个子组件上执行.

我已经找到了一个解Bounce函数的解决方案,它接受一个额外的id debounceWithId

但问题是,如果我按如下方式指定此函数:

  methods: {
    processData: debounceWithId(function () {
      console.log(this.id)
    }, 250, this.id),

最后一个是这个.id未定义.

在多个组件中使用debounce以使函数在每个组件上分别启动的正确方法是什么?

推荐答案

首先,让我添加一个复制您描述的问题的示例.

console.clear()

function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        console.log("Called from component ", this._uid)
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

Vue.component("doesntwork",{
  props:["value"],
  template:`<div>Component #{{_uid}} Value: {{innerValue}}</div>`,
  data(){
    return {
      innerValue: this.value
    }
  },
  watch:{
    value(newVal){
      this.processData(newVal)
    }
  },
  methods:{
    processData: debounce(function(newVal){
      this.innerValue = newVal
    }, 1000)
  },
})


new Vue({
  el: "#app",
  data:{
    parentValue: null,
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://unpkg.com/vue@2.4.2"></script>
<div id="app">
  Type some text. Wait one second. Only the *last* component is updated.<br>
  <input type="text" v-model="parentValue">
  <doesntwork :value="parentValue"></doesntwork>
  <doesntwork :value="parentValue"></doesntwork>
  <doesntwork :value="parentValue"></doesntwork>
</div>

本质上,这里要做的是在编译组件时创建go Bounced函数,组件的每个实例共享samego Bounced函数.this的上下文在每一个中都是不同的,但它是相同的函数.我在生成的go Bounced函数中添加了console.log,这样您就可以看到所有三个组件共享相同的函数.在这种情况下,函数正在执行其设计的任务;它在经过一段时间后执行once,这就是为什么只更新最后一个组件.

为了避免这种行为,每个组件都需要一个unique次的go 盎司函数.这里有两种方法可以做到这一点.

Method One

可以使用相当于占位符的内容初始化processData方法.

methods: {
  processData(){}
}

然后,在创建的生命周期事件中,将processData方法更改为debounced方法.

created(){
  this.processData = debounce(function(){
    console.log(this.id)
  }, 250)
}

这将 for each 组件提供一个独特的debounce 功能,并应解决只有last个组件正常工作的问题.

下面是根据上述示例修改的示例.

console.clear()

Vue.component("works",{
  props:["value"],
  template:`<div>Component #{{_uid}} Value: {{innerValue}}</div>`,
  data(){
    return {
      innerValue: this.value,
    }
  },
  watch:{
    value(newVal){
      this.processData(newVal)
    }
  },
  methods:{
    processData() {}
  },
  created(){
    this.processData = _.debounce(function(newVal){
      this.innerValue = newVal
    }, 1000)
  }
})

new Vue({
  el: "#app",
  data:{
    parentValue: null,
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://unpkg.com/vue@2.4.2"></script>
<div id="app">
  Type some text. Wait one second. <em>All</em> components are updated.<br>
  <input type="text" v-model="parentValue">
  <works :value="parentValue"></works>
  <works :value="parentValue"></works>
  <works :value="parentValue"></works>
</div>

Method Two

感谢@RoyJ的建议.您可以在data中定义processData方法.通常不这样做是因为通常不需要一个函数的多个副本,这就是组件定义的methods部分存在的原因,但在这样的情况下,您可以 for each 组件定义一个唯一的函数,您可以在数据函数中定义方法,因为数据函数是为组件的every实例调用的.

data(){
  return {
    innerValue: this.value,
    processData: _.debounce(function(newVal){
      this.innerValue = newVal
    }, 1000)
  }
},

下面是一个使用这种方法的例子.

console.clear()

Vue.component("works",{
  props:["value"],
  template:`<div>Component #{{_uid}} Value: {{innerValue}}</div>`,
  data(){
    return {
      innerValue: this.value,
      processData: _.debounce(function(newVal){
        this.innerValue = newVal
      }, 1000)
    }
  },
  watch:{
    value(newVal){
      this.processData(newVal)
    }
  },
})

new Vue({
  el: "#app",
  data:{
    parentValue: null,
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://unpkg.com/vue@2.4.2"></script>
<div id="app">
  Type some text. Wait one second. <em>All</em> components are updated.<br>
  <input type="text" v-model="parentValue">
  <works :value="parentValue"></works>
  <works :value="parentValue"></works>
  <works :value="parentValue"></works>
</div>

Vue.js相关问答推荐

在Nuxt项目中混合Scrolltrigger与Lenis

如何将模板传递给EJS菜单

在移动版本中使用标准的 v-data-table

如何在vue js模板中添加foreach和for循环

何时将代码拆分为 Nuxt 中的组件?

Vue:根元素内容和内容分发槽

在 Vue 中启用(控制台)记录路由事件

在重复内容区域中添加

使用 v-slot:body 时 Vuetify v-data-table 没有响应

如何在 Vue.js 中添加动态组件/部分

React.js 是否有类似 Vue.js

VueJS 组件中的图像未加载

是否可以在 vue-devtools 中命名 Vue 实例?

错误:[vuex] 期望字符串作为类型,但发现未定义

Laravel 事件未广播

包含 Vue 组件的 HTML 字符串的 Nuxt 渲染函数

如何在生产模式下为 chrome 扩展启用 Vue devtools?

这是 v-on:change 的正确用法吗?

如何使用vue.js/nuxt.js获取一个目录下的所有图片文件

您正在运行 Vue 的 esm-bundler 构建