美文网首页让前端飞
只需一步,让你的搜索框智能得起飞

只需一步,让你的搜索框智能得起飞

作者: 爱编程的小金 | 来源:发表于2021-07-19 18:46 被阅读0次

    以前我总是这样过滤搜索内容

    data.filter(item => item.indexOf(keywords) >= 0)
    

    这样确实可以满足关键字搜索需求,但鉴于前端是直接呈现画面给用户的人,我们总是需要站在用户的角度去考虑问题。后面项目做多了,其实发现这样在用户体验上并不够友好,我们来看下下面这个例子:

    chrome浏览器内编辑样式

    chrome.gif

    vs code内编辑样式

    vscode.gif

    在chrome浏览器内无法进行非连续的字符匹配,造成了一些体验感的缺失,而在vs code内却可以自由间断地输入关键字匹配。毫无疑问在vs code中我们的编辑体验会更加流畅,因为在很多场景下,我们记不得一段连续的关键字,而是在断断续续的关键字,此时我们使用这种间断的关键字进行搜索可以很好地提高用户体验,而且也不影响连续的关键字搜索。

    什么场景下可以用非连续关键字搜索

    这类需求一般为搜索本地数据时,且需求场景没有明确指定需要连续的关键字搜索时,我们都可以使用非连续关键字搜索来实现,具体列举了以下几个场景:

    1. 树形菜单项搜索:类似管理后台页面的菜单、多节点搜索
    2. checkbox选项搜索:选项较多的可搜索checkbox内
    3. 路径列表搜索:如vs code的文件搜索
    4. 本地缓存搜索:微信的缓存聊天记录关键字搜索

    接下来我们探讨一下如何实现

    得益于万能的npmjs.com,经历百般搜索,我在上面找到了一个不错的js函数库实现了非连续关键字搜索,我们先看一个简单的Demo,再剖析一下它的实现原理。

    js库在此 -> string-discontinuous-match

    demo.gif

    此Demo中在1000条长度为200的随机字符串中非连续关键字搜索,完成单次搜索只需要1-2ms,性能可观。

    接下来贴一下实现代码,通过Vue实现

    <div id="app">
      <div class="search-wrap">
        <label>🔍</label>
        <input class="search-box" v-model="val" @input="inputHandler" />
      </div>
      <div class="info">
        <span>Data totals: {{ numbers }}</span>
        <span v-if="performance">Performance: {{ performance }}</span>
      </div>
      <div class="result">
        <span class="item" v-for="item in strings" :key="item" v-html="item"></span>
      </div>
    </div>
    
    // 初始状态
    data() {
      return {
        val: '',
        strings: strings,  // strings内包含1000条测试随机字符串
        numbers: strings.length,
        performance: '',
      };
    }
    

    以下为input事件实现

    <input @input="inputHandler" />
    
    import { discontinuousMatch, replaceMatchedString } from 'string-discontinuous-match';
    function inputHandler() {
      // this.val为搜索关键字
      if (this.val) {
        let s = performance.now();
          let ret = discontinuousMatch(strings, this.val);
          let e = performance.now();
          this.strings = ret.map(item => {
            // 辅助函数replaceMatchedString
            return replaceMatchedString(item, chars => `<span class="keyword">${chars}</span>`);
          });
          this.performance = (e - s) + 'ms';
        }
        else {
          this.data = strings;
          this.strings = strings;
          this.performance = '';
        }
        this.numbers = this.strings.length;
      }
    

    搜索结果格式为:

    [
      {
        value: 'xxx',   // 被搜索的字符串
        index: 0,       // 该项在数组中的索引
        
        // 匹配的关键字索引位置,如[0, 2]表示value中下标为0-2(包含2)位置的字符,14表示下标为14的单个字符。
        position: [[0, 2], [10, 12], 14, 17],
        lastIndex: 17   // 最后一个匹配的索引
      },
      // ...
    ]
    

    所以你可以通过position信息去高亮对应位置的字符,但实际上你并不需要自己处理,string-discontinuous-match为我们提供了一个辅助函数来处理它,例如在inputHandler函数中,你可以看到将匹配结果循环传入了replaceMatchedString函数中,它的作用是帮我们提取匹配到的字符串,它根据position数组依次触发回调,在position: [[0, 2], [10, 12], 14, 17]中会分别提取下标0到2的字符串,并触发一次回调,在回调中你可以返回转化后的字符串例如用<span>标签包裹它,接下来再提取10到12的字符串并触发回调,以此类推共计触发4次回调,最后把转化后的字符串返回给我们,这样我们就可以很方便地实现匹配关键字的高亮状态了。

    通过这样一顿操作,就已经实现了非连续关键字搜索的功能了。

    性能怎么样???

    大家应该都会有这样的疑问,数据量小还好,但对于大数据量的匹配,会不会很慢呢?毕竟是非连续的匹配呢!!!
    这个问题,此js库给出了这样的回答:

    image.png
    在10000个5000位随机字符串中搜索50位随机关键字符串,忽略大小写,花了101ms,性能好是不错的。

    结尾

    对于一个前端来说,我们还是应该把用户体验放在重要的位置,而这个搜索就是其中的优化点之一,只需要简单一步操作,我们就可以解决此问题,不知道正在看文章的你觉得是否值得一试呢?如果对这个js库感兴趣的可以自行研究源码,其实它也很简单。
    今天就到此啦,如有什么问题可以给作者去提issue哦。对我的教程感到满意,请不要吝啬你手中的免费赞赞哦!!!

    相关文章

      网友评论

        本文标题:只需一步,让你的搜索框智能得起飞

        本文链接:https://www.haomeiwen.com/subject/qtvhmltx.html