Github地址:github.com/DPDFE/antd-… ~欢迎Issues和Star
Features:
- 无需修改业务代码,一键接入antd Select拼音搜索
- 支持拼音、拼音首字母模糊搜索
- 支持拼音、汉字、英文组合搜索
- 支持排序结果排序 TODO:
- 支持更多antd组件的拼音搜索
前言
团队内的前端项目一直有一个用户体验问题:不支持JS拼音搜索。几乎所有UI库中带搜索功能的组件都只能配合后端来实现拼音搜索。尤其是当同一页面支持拼音的后端搜索与不支持拼音的前端匹配搜索混用时,用户体验非常的割裂。
我们的中后台项目中大量用到了antd的Select组件,然而antd-select的搜索功能却不尽如人意,当开启showSearch时,默认使用的是includes做全量匹配,只能实现汉字搜索汉字的功能,如图
让antd-select支持拼音搜索的几种办法
filterOption API
antd-select暴露出了filterOption API,我们可以传入一个函数对搜索结果进行自定义筛选,如下
<Select
showSearch
optionFilterProp="children"
filterOption={(input, option) =>
pinYinFuzzSearch(input,[option]).length>0
}
>
<Option value="jack">Jack</Option>
</Select>
利用这个API,可以实现一个简单的Pinyin-Select,但这种方式本质上是向filter中传入了filterFun Array.filter(filterFun),在filterFun里一遍遍的调用拼音搜索会带来一定的性能损耗(每次都会对input重新分词)。
每个使用到select的地方都得用filterOption添加上搜索代码,若想对搜索结果按规则排序,还需要存储搜索结果并结合input的内容调用另一个filterSort API来实现。
rc-select
antd-select的底层使用了rc-select,找到rc-select源码中filterOption执行的地方添加上拼音搜索的代码就可以实现拼音搜索,基于这个思路,我们对rc-select代码改造并发包了rc-pinyin-select
function filterOptions(
searchValue: string,
options: SelectOptionsType,
{
optionFilterProp,
filterOption,
}: {
optionFilterProp: string;
filterOption: boolean | FilterFunc<SelectOptionsType[number]> | 'pinyin';
},
) {
if (filterOption === 'pinyin') {
const res = pinYinFuzzSearch(searchValue, options, {
textProvider: (item) => {
// Group should check child options
if ('options' in item) {
const matchGroup = pinYinFuzzSearch(searchValue, [item], {
textProvider: (i) => i[optionFilterProp].toString(),
});
if (matchGroup.length) {
filteredOptions.push(item);
} else {
const subOptions: any = pinYinFuzzSearch(searchValue, item.options, {
textProvider: (i) => i[optionFilterProp].toString(),
});
if (subOptions.length) {
filteredOptions.push({
...item,
options: subOptions,
});
}
}
}
return toRawString(item[optionFilterProp]);
},
});
}
}
使用rc-pinyin-select需在webpack的alias项中配置 alias:{'rc-select': '@dpdfe/rc-pinyin-select'} 这样webpack查找依赖时,就会去找我们改造过的rc-pinyin-select。这种方式可以使项目中原有开启showSearch的Select自动支持拼音搜索,避免逐一修改代码。
由于是在代码里直接返回拼音搜索的匹配项数组,可以对搜索结果进行自定义的排序。
但不同项目中antd版本不同,依赖的rc-select版本也不相同,我们的rc-select基于最新的代码改造,可能会有兼容性问题。
antd-pinyin-select
antd还有另一个API dropdownRender,供我们自定义下拉框内容,利用这个API可以获取到所有的下拉选项,这种方式只需进行一次搜索就可以得到结果,并且可以对结果排序。
这种方式的缺点和使用filterOption API的方式相同,每处用到select的地方都需要手动添加dropdownRender以支持拼音搜索。为解决这个问题,我们基于antd封装了antd-pinyin-select对dropdownRender进行了统一处理,使用时配合babel import插件可实现旧select代码零修改支持拼音搜索。由于引入的是宿主应用中的antd-select,只要antd版本支持这个API,理论上可以和未来的antd-select版本也可无缝兼容,TS类型也能正确指示。
// file: babel.config.js
{
"plugins":[
[
"import",
{
libraryName: "antd",
style: true,
customName: (name, file) => {
if (name === "select") {
return "@dpdfe/antd-pinyin-select/es";
}
return `antd/es/${name}`;
},
}
]
]
}
除此之外,我们还扩充了原生Select不具有的拼音搜索参数,为保证类型提示正确,使用拼音搜索参数时需要从我们的包中引入
import {Select} from '@dpdfe/antd-pinyin-select';
function App() {
// sort、separator等为在Antd Select的基础上扩充的可选参数
return <Select sort="DESC" separator=",">
...
</Select>
}
拼音搜索的原理
上述方法方法还需要配合一个强大的JS拼音搜索引擎,这里使用了团队开源的拼音搜索工具,欢迎试用
import {pinYinFuzzSearch} from '@dpdfe/tools'
pinYinFuzzSearch('shizhangsan', ['是张三', '是李四']); // output: ['是张三']
Github地址:github.com/DPDFE/tools
Features
- 支持拼音模糊搜索
- 支持拼音、汉字、英文组合搜索
- 支持排序结果排序
- 支持多词同时搜索
- 支持设置多词同时搜索时的排序策略和分隔符
- 支持返回匹配位置
拼音匹配的核心思路
- 建立常见汉字的拼音索引,利用word-break算法,对输入的拼音(input)拆分,得到所有可能的分词可能性
- 待匹配列表(list)转化成拼音
- 进行首字母匹配和完全匹配
但输入可能有多种情况
- 只输入拼音 | 首字母
- 只输入中文
- 拼音 | 首字母和中文混输
- 其它字符或带有其它字符的混输
同样的情况也可能会出现在待匹配列表中,针对这些情况的用例详见:github.com/DPDFE/tools…
为了简化,我们会先将所有中文转换为拼音,其它字符保留原样,再进行匹配,这样匹配可能会有相同读音汉字的异常匹配结果,但对绝大多数应用场景,用户并不会将拼音和汉字混输。
参考: