在将参数传递给去抖函数时限制或去抖 Vue 2 中的异步调用

Mat*_*gan 5 javascript throttling vue.js vuejs2

我有一个 Vue 2 应用程序,它使用对象数组来支持vue-multiselect提供的搜索/多选小部件。

我已经查看了关于去抖动调用的Vue 1 -> 2 迁移指南,但是他们给出的示例没有将参数从 DOM 元素传播到业务逻辑。

现在,每次击键时 select 都会触发更改事件,但我想限制它(例如,使用 lodash#throttle),这样我就不会在他们打字时每隔几毫秒就调用我的 API。

import {mapGetters} from 'vuex';
import { throttle } from 'lodash';

import Multiselect from 'vue-multiselect'

export default {
  components: {
    Multiselect
  },
  data() {
    return {
      selectedWork: {},
      works: [],
      isLoading: false
    }
  },
  computed: {
    ...mapGetters(['worksList']),
  },
  methods: {
    getWorksAsync: throttle((term) => {
      // the plan is to replace this with an API call
      this.works = this.worksList.filter(work => titleMatches(work, term));
    }, 200)
  }
}
Run Code Online (Sandbox Code Playgroud)

问题:当用户在选择框中键入内容时,出现错误:

TypeError: Cannot read property 'filter' of undefined
Run Code Online (Sandbox Code Playgroud)

这是因为this.worksListundefined在函数内部发生的throttle

奇怪的是,当我使用开发工具调试器时,this.worksList有我需要取消引用的值,this引用 Vue 组件来取消引用该值。

目前我没有从组件内调用 API,但问题仍然相同:

  1. 我怎样才能限制这个调用,并有适当的this上下文来更新我的this.works列表?编辑:这在使用 axios 时 Vue Watch 不会触发中进行了解释
  2. 我还需要从多选小部件中捕获用户的查询字符串以传递给 API 调用。

Vue 2 中正确的模式是什么?

Mat*_*gan 1

我无法找到有关此问题(或任何地方)的答案,但我最终通过反复试验以及此处和文档中的相关材料将其拼凑在一起。

我没有做过但有效的事情,以及为什么

可以使用 JavaScript DOM 查询直接获取值,也可以深入研究多选组件的结构并获取值。第一个解决方案绕过了框架,第二个解决方案依赖于多选组件的未记录属性。我避免使用这两种解决方案,因为它们不惯用且脆弱。

我目前的解决方案

  1. 每当搜索框中发生更改事件时,就会更新组件上的属性。这使我能够捕获用户的查询字符串。
  2. 从事件侦听器内部调用我的节流异步函数。
  3. 将常规function函数而不是箭头函数传递给throttle,这给出了正确的this(Vue 组件。)

如果有人有关于在 Vue 2 中实现此操作的更好方法的建议,我会洗耳恭听。

这是我的解决方案最终的样子:

<template>    
  <div>
    <label
      class="typo__label"
      for="ajax">Async select</label>
    <multiselect
      id="ajax"
      v-model="selectedWork"
      label="title"
      track-by="id"
      placeholder="Type to search"
      :options="works"
      :searchable="true"
      :loading="isLoading"
      :internal-search="false"
      :multiple="false"
      :clear-on-select="true"
      :close-on-select="true"
      :options-limit="300"
      :limit="3"
      :limit-text="limitText"
      :max-height="600"
      :show-no-results="false"
      open-direction="bottom"
      @select="redirect"
      @search-change="updateSearchTerm">
      <span slot="noResult">Oops! No elements found. Consider changing the search query.</span>
    </multiselect>
  </div>
</template>

<script>
  import {mapGetters} from 'vuex';
  import { throttle } from 'lodash';

  import Multiselect from 'vue-multiselect'

  export default {
    components: {
      Multiselect
    },
    data() {
      return {
        searchTerm: '',
        selectedWork: {},
        works: [],
        isLoading: false
      }
    },
    computed: {
      ...mapGetters(['worksList']),
    },
    methods: {
      limitText(count) {
        return `and ${count} other works`;
      },
      redirect(work) {
        // redirect to selected page
      },
      updateSearchTerm(term){
        this.searchTerm = term;
        this.isLoading = true;
        this.getWorksAsync();
      },
      getWorksAsync: throttle(function() {
        const term = this.searchTerm.toLowerCase();
        callMyAPI(term)
        .then(results => {
            this.works = results;
            this.isLoading = false;
        })
      }, 200)
    }
  }

</script>
Run Code Online (Sandbox Code Playgroud)