【问题标题】:VueJS 2 debounce on multiple componentsVueJS 2 在多个组件上去抖动
【发布时间】:2018-02-05 02:59:18
【问题描述】:

我有一个使用多个子组件的 Vue 组件。在这些子组件上,我有一个观察者来观察数据变化并处理这些变化。我想为此实现去抖动。

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

问题在于 debounce 有效,因此它只在最后一个子组件上执行。

我找到了一个接受额外 id debounceWithId 的 debounce 函数的解决方案

但是有一个问题是,如果我指定这个函数如下:

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

最后一个 this.id 是未定义的。

在多个组件中使用 debounce 以便函数在每个组件上单独触发的正确方法是什么?

【问题讨论】:

    标签: vue.js quasar debounce


    【解决方案1】:

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

    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>

    基本上这里发生的事情是在编译组件时创建去抖函数,并且组件的每个实例共享相同去抖函数。 this 的上下文在每一个中会有所不同,但它是相同的功能。我在生成的去抖动函数中添加了一个console.log,以便您可以看到所有三个组件共享相同的函数。在这种情况下,该功能正在执行它的设计目的;它在经过一段时间后执行一次,这就是为什么只更新最后一个组件的原因。

    要解决该问题,您需要为每个组件设置一个独特 去抖动功能。您可以通过以下两种方式做到这一点。

    方法一

    您可以使用占位符来初始化您的 processData 方法。

    methods: {
      processData(){}
    }
    

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

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

    这将为每个组件提供一个独特的去抖动功能,并且应该解决只有 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>

    方法二

    感谢@RoyJ 提出这个建议。您可以在data 中定义processData 方法。通常你不会这样做,因为你不经常想要一个函数的多个副本,这就是为什么存在组件定义的 methods 部分的原因,但在这种情况下,你 需要每个组件都有一个唯一的函数,你可以在数据函数中定义方法,因为数据函数是为组件的每个实例调用的。

    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>

    【讨论】:

    • @RoyJ 你有什么想法?在 codepen 中,我并没有真正返回一个值,只是更新了一个数据属性。在非工作组件中,去抖动方法甚至只触发一次。这……让我有点恍惚。所有组件都使用相同的函数定义,其要点是在周期结束后触发一次
    • 对于它的价值,computeds 的工作方式相同:如果您将 debounce 放入 setter 中,它将对所有实例进行一次去抖动。这是一个有点微妙的意外行为,但如果您的代码是 DRY,它应该很少见。在示例中,我们制作了多个数据副本;去抖动可能应该在父级中完成并作为道具传递。
    • 同样 FWIW,您可以data 部分定义 processData,每个实例都会在其中创建一次。
    • @RoyJ 我希望使用 debounced 的任何东西(在数据函数之外)都会遇到同样的问题。顺便说一句,这是关于数据功能的一个很好的想法;它确实是一个工厂功能,符合您昨晚的想法。这不直观,但我同意,它也应该可以解决问题。
    • 谢谢 - 现在它按我的预期工作了。我正在使用方法二,因为它看起来更简洁,并且在代码中添加的行更少。
    【解决方案2】:

    另一个对我有用的解决方案是使用$watch api 在组件创建后添加一个观察器,然后组件之间不会共享去抖动功能

    created () {
      this.$watch(
        'foo',
        debounce(function bar() {
          // do something
        },
        {}
      )
    }
    

    【讨论】:

      猜你喜欢
      • 2017-06-16
      • 2017-05-09
      • 2015-11-10
      • 2016-07-31
      • 1970-01-01
      • 2021-06-30
      • 1970-01-01
      • 2021-11-10
      • 1970-01-01
      相关资源
      最近更新 更多