Debouncing/throttling 使用 Vue 的方法 Class 组件语法

Debouncing/throttling a method using Vue Class Component Syntax

我正在开发一个组件,当搜索栏中的文本发生变化时,该组件会查询外部 API,我正在尝试对该查询进行去抖动处理,使其只能每 2 秒执行一次。我正在尝试使用 lodash 的 debounce 函数来做到这一点,并且发现了多篇博文和关于将它与 Vue 组件一起使用的问题,但事情很复杂,因为我使用的是 Typescript 和 Vue Class组件语法 (https://class-component.vuejs.org/)。老实说,我对这两个都很陌生。

我找到了 a blog post outlining how to do this with object-based Vue component syntax,但它不适用于 Class 组件语法。基于对象的语法允许您将方法包装在 _.debounce 中,如下所示:

export default {
  methods: {
    throttledMethod: _.debounce(() => {
      console.log('I only get fired once every two seconds, max!')
    }, 2000)
  }
}

有没有办法用 Vue Class 组件语法做类似的事情?

以下是我的代码的相关部分(没有任何去抖动尝试):

<template>
  <input
    v-model="searchQuery"
    @keydown="doSearch"
  >
</template>

<script lang="ts">
import axios from 'axios';
import _ from 'lodash';
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class FooSearch extends Vue {
  // data
  searchQuery = '';
  results = [];

  // methods
  async doSearch() {
    try {
      const response = await axios.get('https://api.example.org/search', {
        params: {
          query: this.searchQuery,
        }
      });

      this.results = response.data.results;
    } catch(error) {
      console.log('error');
      console.log(error);
    }
  }
</script>

已讨论here

基本上,您需要定义基本函数(就像您对 doSearch 所做的那样),然后定义新的去抖动函数:

public doSearchDebounced = _.debounce(this.doSearch, 2000)

现在你只需要调用 doSearchDebounced 而不是 doSearch

你可以这样做

<script lang='ts'>   
 import { debounce } from 'decko'

@debounce(1000)
 async doSearch() {
....
}
</script>

虽然上面已经回答了,但是我觉得还是应该好好利用vue-class-component中的createDecorator,整合lodash来简化Debounce和Throttle的使用

创建decorator.ts


import { createDecorator } from "vue-class-component";
import _ from "lodash";

export const Debounce = (waitMs: number) =>
  createDecorator((options, key) => {
    if (options.methods && options.methods[key]) {
      const originalMethod = options.methods[key];
      const debounceMethod = _.debounce(originalMethod, waitMs, {
        leading: false,
        trailing: true,
      });

      options.methods[key] = async function (...args: any) {
        await debounceMethod.apply(this, args);
      };
    }
  });

export const Throttle = (waitMs: number) =>
  createDecorator((options, key) => {
    if (options.methods && options.methods[key]) {
      const originalMethod = options.methods[key];
      const throttleMethod = _.throttle(originalMethod, waitMs, {
        leading: true,
        trailing: false,
      });

      options.methods[key] = async function (...args: any) {
        await throttleMethod.apply(this, args);
      };
    }
  });

重构代码并使用装饰器。

<template>
  <input
    v-model="searchQuery"
    @keydown="doSearch"
  >
</template>

<script lang="ts">
import axios from 'axios';
import { Component, Vue } from 'vue-property-decorator';
import { Debounce } from "@/decorator";

@Component
export default class FooSearch extends Vue {
  // data
  searchQuery = '';
  results = [];

  // add the Debounce annotation
  @Debounce(1500)
  async doSearch() {
    try {
      const response = await axios.get('https://api.example.org/search', {
        params: {
          query: this.searchQuery,
        }
      });

      this.results = response.data.results;
    } catch(error) {
      console.log('error');
      console.log(error);
    }
  }
</script>