Bootstrap

从覆盖到拼接:优化 onInput 事件的输入

在使用 ElSelect 组件的 onInput 事件时,由于每次输入都触发搜索,导致请求频繁且新搜索结果覆盖了旧结果,无法实现输入数据的累积搜索。我们希望的是,每次搜索能够将新的输入内容与之前的内容拼接显示,从而实现用户的诉求。

ElSelect 组件

<ElSelect
  ref={storeRef}
  filterable
  remote
  remoteMethod={getSiteList}
  multiple
  clearable
  v-model={queryForm.value.pickUpSiteIdList}
  placeholder="请输入门店名称"
  onInput={(e) => { handleInput(e?.data || '', SelectEnum.store); }}
  >
  {siteList.value?.map((item) => (<ElOption label={item.siteName} value={item.siteId} />))}
</ElSelect>

onInput 事件

const handleInput = (query, type) => {
  if (type === SelectEnum.store) {
    storeRef.value.remoteMethod(query);
  }
};

// 其余
const getSiteList = async (query = '') => {
  if (query) {
    const res = await querySiteList({
      siteName: query,
    });
    siteList.value = res.records;
  } else {
    siteList.value = [];
  }
};

目前现状展示,后面搜索的结果覆盖之前的搜索结果o(╥﹏╥)o。

可能第一反应就是,使用函数防抖,OK。

import { debounce } from 'lodash';

const handleInput = debounce((query, type) => {
  if (type === SelectEnum.store) {
    storeRef.value.remoteMethod(query);
  }
}, 500);

展示为:

虽然效果好一些,但仍不是我们想要的结果,我希望搜索的是「上海南」,而不是「上海」「南」 。

可能会觉得,时间拉长点,会不会好一点,展示一下:

const handleInput = debounce((query, type) => {
  if (type === SelectEnum.store) {
    storeRef.value.remoteMethod(query);
  }
}, 2000);

显而易见,更加糟糕,因为间隔时间变长,对应的搜索也就更加精确,最后一次搜索就是「站」。

不妨换一个思路:

将之前的输入与当前输入拼接起来,每次用户输入时,等一小段时间再发起请求,这样可以减少请求次数并避免连续输入时产生多个请求。 

const searchContent = ref(''); // 当前输入框内容,拼接所有输入的内容
// 防抖定时器
let timeoutId = null;

const handleInput = (query, type) => {
  // 处理输入拼接
  searchContent.value += query;

  // 清除之前的定时器,防止连续输入时发送多个请求
  if (timeoutId) {
    clearTimeout(timeoutId);
  }
  // 使用防抖,延迟搜索
  timeoutId = setTimeout(() => {
    if (type === SelectEnum.store) {
      storeRef.value.remoteMethod(searchContent.value);
    }
  }, 500); // 500ms 的防抖延迟
};

运行发现一个比较诧异的现象,输入内容包括那么多拼音,也不是我们想要的。 

再改进一下:

需要解决 累积拼接输入时产生的错误!!!这种问题通常是因为 onInput 事件触发时处理不当,导致历史内容和当前输入被错误拼接。

解决方法:以输入框当前值为准!

// 防抖定时器
let timeoutId = null;

const handleInput = (query, type) => {
  // 清除之前的定时器,防止连续输入时发送多个请求
  if (timeoutId) {
    clearTimeout(timeoutId);
  }
  // 防抖延迟
  timeoutId = setTimeout(() => {
    nextTick(() => {
      if (type === SelectEnum.store) {
        const inputValue = storeRef.value?.$el.querySelector('input')?.value || '';
        storeRef.value.remoteMethod(inputValue);
      }
    });
  }, 500); // 防抖延迟 500ms
};

代码解释:

每次 onInput 事件触发时,直接使用 storeRef 引用直接获取输入框中当前的完整值,确保内容是正确的,而不是依赖传入的 onInput 参数或直接拼接,这避免了累积拼接错误。

使用 nextTick,确保在 DOM 更新后获取到最新的输入框值。

OK,到处实现完美解决!  

;