我打算做一个信息检索类的应用,其中在当前页面,它会调用到一个消息搜索的逻辑,可能需要将搜索得到的关键字,高亮有什么现代的API可以实现他吗?
信息检索类应用的高亮实现
ChatGPT
2 条消息
如果你的场景是信息检索 / 消息搜索结果页(类似微信聊天搜索、Google 搜索结果、IDE 全文搜索),现在比较现代的做法主要有几种,取决于你的需求。
方案 1:CSS Custom Highlight API(现在最现代)
现在浏览器有一个专门干这个事情的 API:
CSS Custom Highlight API
它的特点:
- 不需要修改 DOM
- 不需要把文本包成
<span> - 性能比
innerHTML.replace()好很多 - 很适合搜索结果关键词高亮、代码编辑器、文档阅读器
它的流程:
搜索关键词
↓
找到文本位置
↓
创建 Range
↓
Highlight 注册
↓
CSS ::highlight() 渲染
例如:
const textNode = document.querySelector(".message").firstChild;
const range = new Range();
range.setStart(textNode, 5);
range.setEnd(textNode, 10);
const highlight = new Highlight(range);
CSS.highlights.set(
"search-result",
highlight
);
CSS:
::highlight(search-result) {
background: yellow;
color: black;
}
效果:
今天去了北京旅游
^^^
北京
而原始 DOM 还是:
<div>
今天去了北京旅游
</div>
不会变成:
<div>
今天去了<span>北京</span>旅游
</div>
这对于消息列表非常舒服。(MDN Web Docs)
方案 2:传统 span 包裹(最常见)
很多项目目前仍然这么做:
搜索:
关键词: React
原文本:
今天学习 React 很开心
处理:
今天学习
<span class="highlight">
React
</span>
很开心
CSS:
.highlight {
background:#ffe58f;
}
优点:
- 简单
- 兼容性最好
- React/Vue 很容易实现
缺点:
如果你的消息:
100万条聊天记录
每条几千字
大量:
<span>
会导致:
- DOM 膨胀
- React diff 压力
- 复制文本处理麻烦
所以大型搜索系统一般不会这么干。
方案 3:后端返回 highlight 片段(搜索引擎方案)
如果你的搜索后端是:
- Elasticsearch
- Meilisearch
- Typesense
- OpenSearch
通常直接让搜索引擎返回:
{
"content": "今天去了 <em>北京</em>旅游"
}
例如 Elasticsearch 有:
highlight
功能。
流程:
用户搜索
|
v
搜索服务
|
v
返回:
{
text:"今天去了北京旅游",
highlights:[
{
start:4,
end:6
}
]
}
|
v
前端渲染
这种更适合你的场景。
如果你做的是「聊天记录检索」(结合你之前那个消息存储产品)
我会推荐:
数据结构:
搜索服务返回:
{
"messageId":123,
"content":"今天去了北京旅游",
"matches":[
{
"keyword":"北京",
"offset":4,
"length":2
}
]
}
前端:
Message Component
|
|
Highlight Layer
|
|
CSS Custom Highlight API
类似:
┌─────────────────────┐
│ 今天去了 北京 旅游 │
│ ↑ │
│ highlight │
└─────────────────────┘
优点:
- 消息内容保持纯文本
- 搜索结果可以缓存
- 可以支持多个关键词
- 可以支持跳转下一个匹配
React 里面怎么封装
大概会做成:
<MessageHighlight
text="今天去了北京旅游"
ranges={[
{
start:4,
end:6
}
]}
/>
内部:
useEffect(()=>{
const range = new Range();
range.setStart(node,start);
range.setEnd(node,end);
CSS.highlights.set(
"search",
new Highlight(range)
);
},[])
兼容性问题
CSS Custom Highlight API 是比较新的标准,目前现代浏览器支持情况已经不错,但老浏览器可能不支持。(MDN Web Docs)
生产项目一般:
if(CSS.highlights){
useModernHighlight()
}else{
fallbackToSpan()
}
如果你的目标是做类似 微信聊天搜索 / Notion 全文搜索 / Obsidian 搜索 这种应用,我会倾向:
后端返回 offset → 前端 CSS Custom Highlight API
这是目前架构上最干净的一套。你之前设计的消息存储系统也比较适合这种模型。