以前我总是这样过滤搜索内容
data.filter(item => item.indexOf(keywords) >= 0)
这样确实可以满足关键字搜索需求,但鉴于前端是直接呈现画面给用户的人,我们总是需要站在用户的角度去考虑问题。后面项目做多了,其实发现这样在用户体验上并不够友好,我们来看下下面这个例子:
chrome浏览器内编辑样式
vs code内编辑样式
在chrome浏览器内无法进行非连续的字符匹配,造成了一些体验感的缺失,而在vs code内却可以自由间断地输入关键字匹配。毫无疑问在vs code中我们的编辑体验会更加流畅,因为在很多场景下,我们记不得一段连续的关键字,而是在断断续续的关键字,此时我们使用这种间断的关键字进行搜索可以很好地提高用户体验,而且也不影响连续的关键字搜索。
什么场景下可以用非连续关键字搜索
这类需求一般为搜索本地数据时,且需求场景没有明确指定需要连续的关键字搜索时,我们都可以使用非连续关键字搜索来实现,具体列举了以下几个场景:
- 树形菜单项搜索:类似管理后台页面的菜单、多节点搜索
- checkbox选项搜索:选项较多的可搜索checkbox内
- 路径列表搜索:如vs code的文件搜索
- 本地缓存搜索:微信的缓存聊天记录关键字搜索
接下来我们探讨一下如何实现
得益于万能的npmjs.com
,经历百般搜索,我在上面找到了一个不错的js函数库实现了非连续关键字搜索,我们先看一个简单的Demo,再剖析一下它的实现原理。
js库在此 -> string-discontinuous-match
此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库给出了这样的回答:
在10000个5000位随机字符串中搜索50位随机关键字符串,忽略大小写,花了101ms
,性能好是不错的。
结尾
对于一个前端来说,我们还是应该把用户体验放在重要的位置,而这个搜索就是其中的优化点之一,只需要简单一步操作,我们就可以解决此问题,不知道正在看文章的你觉得是否值得一试呢?如果对这个js库感兴趣的可以自行研究源码,其实它也很简单。
今天就到此啦,如有什么问题可以给作者去提issue哦。对我的教程感到满意,请不要吝啬你手中的免费赞赞哦!!!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。