使用 v-debounce 覆盖突变的输入值

Input value overridden on mutation with v-debounce

考虑以下示例:

https://codesandbox.io/s/vdebounce-input-override-ej4nz

它是大型应用程序的简化版(大部分是模拟版),使用 Observables 根据用户输入从多个 API 获取过滤结果。我需要去抖输入。
在应用程序中,去抖动值较大,因此问题不太明显,但我在示例中降低了它,以便更容易发现问题。我还尝试模拟服务器响应时间的随机性。

问题是每当我收到回复时,如果我正在输入,我会丢失一些字符,具体取决于我输入的速度。
如果我删除 v-debounce 指令(或 .lazy 修饰符),问题就消失了,但没有去抖动。

我也在 v-debounce 回购中提出了它。

我知道我可以完全放弃 v-debounce 并在手表内使用 lodash 的去抖动,但我希望我不必那样做(因为 v-debounce 应该是为 Vue 服务,对吧!?)。到目前为止,我已经尽量保持 "Vue" 的水平


重要编辑:(在回答后)似乎有两个包定义了一个 v-debounce 指令。好的在这里:npm, github.

npm i vue-debounce

我使用的是 v-debounce 并覆盖输入。

了解问题

您遇到的问题是 标记渲染 事件去抖动 之间的相互作用(这可能是 v-debounce 或预期的行为意见可能会有所不同):

  • 您正在对输入元素上 change 事件 的触发进行去抖动
    • 去抖动事件触发调用时的输入值(也就是说,它在去抖动调用中没有调用数据的记忆)
  • 另外(在每次实际调用去抖动时)你有一个异步操作(模拟服务器搜索)改变组件的状态,并触发一个组件re-render
    • 这个 re-render 检查整个组件 DOM,"corrects" 它认为输入值是错误的
  • 当异步操作后触发去抖动更改事件时,它会使用 当前 DOM 输入值 触发,该值现在已经过时(甚至有些混乱) , 如果你一直打字)

可能的解决方案

这里的主要问题是应该跟踪实际搜索值。您想要去抖搜索操作,而不是值更改本身(当然也不是输入更改事件)

我们自己处理去抖

Here is an alternative to your app,执行以下操作:

  • 保持 v-model - search - 无去抖动
  • 关于 v-model 的更改:
    • 如果当前正在进行搜索,会立即取消
    • 触发 去抖动搜索操作(只有在输入停止时才会真正开始)
[...]
  mounted() {
    this.debouncedSearch = _.debounce(this.search, 500);
  },
  methods: {
    search() {
      this.loading = true;
      this.$searchTimeout = setTimeout(() => {
        store.albums = albums.filter(
          album => album.title.indexOf(this.term) > -1
        );
        store.searchTerm = this.term;
        this.loading = false;
      }, Math.floor(Math.random() * 1000 + 300));
    }
  },
  watch: {
    term() {
      if (this.$searchTimeout) {
        this.loading = false;
        clearTimeout(this.$searchTimeout);
        delete this.$searchTimeout;
      }
      this.debouncedSearch();
    }
  }

不影响v-model

的去抖指令

Here is another solution 使用自定义指令去抖动输入事件处理程序, 不会弄乱 v-model 绑定到 change

这里是应用程序:

  • 保持 v-model - search - 无去抖动
    • 更改上述 v-model,如果当前正在进行搜索,立即取消
  • 使用v-debounce指令
  • 触发去抖动搜索操作(只有在输入停止时才会真正开始)
<input 
  v-model="term" 
  v-debounce="{ delay: 500, handler: search}" 
  @input="clearSearch">