纯前端搜索常规实现就是字符串匹配(includes/indexOf), 使用模糊搜索和拼音搜索可以让搜索体验更好,模糊搜索推荐 fuse.js,拼音搜索推荐 pinyin-match。这两个库都不大,pinyin-match
27KB,fuse.js
不到20KB,体积负担不重,比起引入一个庞大的拼音库轻量很多。
如果再能高亮匹配字符就更完美了。模糊搜索fuse.js可以直接字符正则匹配替换,拼音搜索pinyin-match会返回位置信息,根据位置信息截取字符串即可。
以下是针对 Vue3 的实现代码:
js
let Fuse;
let Pinyin;
const searchValue = ref('');
const list = ref([]);
const searchList = computed(() => {
list.value.forEach(item => (item.titleSearch = void 0));
if (!searchValue.value) return list.value;
try {
// 模糊搜索
const fuse = new Fuse(list.value, {
keys: ['title'],
threshold: 0.5,
});
const fuseList = fuse.search(searchValue.value).map(m => {
m.item.titleSearch = m.item.title.replace(new RegExp(`[${searchValue.value}]`, 'gi'), s => `<span class="search-target">${s}</span>`);
return m.item;
});
// 拼音搜索
const pinyinList = list.value.filter(m => {
if (fuseList.find(f => f.objId === m.objId)) return false;
const match = Pinyin.match(m.title, searchValue.value);
if (match) {
console.log(m.title, match);
m.titleSearch = `${m.title.slice(0, match[0])}<span class="search-target">${m.title.slice(match[0], match[1] + 1)}</span>${m.title.slice(
match[1] + 1
)}`;
}
return match;
});
return fuseList.concat(pinyinList);
} catch (error) {
console.log(error);
return list.value.filter(f => f.title.toLowerCase().includes(searchValue.value.toLowerCase()));
}
});
onMounted(() => {
importSearchLib();
})
function importSearchLib() {
// 模糊搜索库,异步加载
import('fuse.js')
.then(module => {
Fuse = module.default;
})
.catch(err => {
console.log(err);
});
// 拼音搜索库,异步加载
import('pinyin-match')
.then(module => {
Pinyin = module.default;
})
.catch(err => {
console.log(err);
});
}
-
list.value.forEach(item => (item.titleSearch = void 0));
用于提前声明增加响应式属性 titleSearch,该字段是含高亮匹配字符的富文本。 -
return list.value.filter(f => f.title.toLowerCase().includes(searchValue.value.toLowerCase()));
错误回退机制,如果 fuse.js 或 pinyin-match 使用中报错了,回退到常规的字符串匹配(includes/indexOf)搜索。 -
importSearchLib()
异步加载fuse.js
和pinyin-match
,虽然这两个库都不大,但搜索一般是用户主动触发,没必要在页面加载过程中引入,难免拖慢页面加载,上面的实现就是放到了 onMounted 钩子中等 DOM 装载完毕才去异步加载这两个库。 -
import('fuse.js')
如果出现如下语法格式化错误提醒:
Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'es2022', 'esnext', 'commonjs', 'amd', 'system', 'umd', 'node16', 'node18', or 'nodenext'.ts-plugin(1323)
改一下 tsconfig.json
的配置即可:
json
{
"compilerOptions": {
"module": "ES2020",
"target": "ES2020",
}
}