搜索建议与自动补全
防抖查询、键盘导航、最近搜索、结果分组。
UX 模式进阶
为什么
用户打字慢、拼错、不知有何。即输即建议将查询从 20 键击降到 3,浮现其不知存在的内容并纠正拼写。
Nielsen Norman Group 研究显示用户在离开前平均停留 10-15 秒。清晰的视觉层次和渐进式披露将任务完成率提高 40-60%。
何时使用
任何 >1000 项或用户不知确切词的搜索:产品、文章、人、命令。跳过小固定列表(10 项过滤器)— 下拉足够。
如何做
1. 防抖 — 末次键击后等 150–300ms 再查;勿每键查
2. 最小长度 — 查前要求 2+ 字符;1 字符查询是噪声
3. 缓存 — 每查询缓存结果;重复查询即时
4. 键盘 — ↑↓ 导航、Enter 选、Esc 关、Tab 接受高亮建议
5. 来源 — 混最近搜索、热门查询、实时结果;标每组
6. 高亮 — 粗每建议中匹配子串以让用户见为何匹配
7. a11y — combobox 角色、aria-activedescendant 高亮项、实时区域结果数
8. 加载 — 200ms 后显 spinner 或骨架;勿快查询闪
示例
function SearchBox() {
const [q, setQ] = useState("");
const [highlight, setHighlight] = useState(-1);
const { results, loading } = useSearch(debounce(q, 200)); // 200ms 防抖
return (
<Combobox value={q} onChange={setQ}>
<ComboboxInput aria-label="搜索" />
<ComboboxList>
{loading && <li>加载中…</li>}
{!loading && results.map((r, i) => (
<ComboboxOption
key={r.id}
value={r.title}
aria-selected={i === highlight}
>
<mark>{r.title.slice(0, q.length)}</mark>{r.title.slice(q.length)}
</ComboboxOption>
))}
</ComboboxList>
</Combobox>
);
}
// useSearch — 防抖、缓存、最小长度
function useSearch(q: string) {
const cache = useRef<Map<string, Result[]>>(new Map());
const [results, setResults] = useState<Result[]>([]);
useEffect(() => {
if (q.length < 2) return setResults([]); // 最小长度
if (cache.current.has(q)) return setResults(cache.current.get(q)!);
api.search(q).then((r) => { cache.current.set(q, r); setResults(r); });
}, [q]);
return { results, loading: q.length >= 2 && !cache.current.has(q) };
}
常见问题
- ⚠每键查 — 'hello' 20 请求,后端被锤,UI 卡
- ⚠无键盘导航 — 仅鼠标,高级用户无法 Tab 过建议
- ⚠无匹配高亮 — 用户不见建议为何出现,信任低
最佳实践
- ✓防抖 200ms + 最小 2 字符 — 请求降 10 倍,无单字符噪声
- ✓全键盘支持 — ↑↓ 导航、Enter 选、Esc 关;高级用户友好
- ✓高亮匹配子串 — 用户见为何匹配,建结果信任