毫秒级响应:前端本地搜索的“降维打击”

你一定被原生 IndexedDB 的查询限制气笑过:它只支持前缀匹配IDBKeyRange.bound),想搜"中间字符"?对不起,原生没有 LIKE %keyword%

在 AI Prompt Manager 场景下,用户可能搜"前端"、"报告"、"审计",这些词可能出现在标题中间。如果直接 getAll() 拿出来在 JS 里 filter,数据量上万时,内存占用主线程卡顿会直接让你的"资深"头衔蒙尘。


1. 为什么原生查询不行?

原生 IndexedDB 的索引是 B-Tree 结构,它能极快地找到"以某字符开头"的数据,但无法处理"包含某字符"。

  • 低级做法getAll() + Array.prototype.filter。数据量 10k+ 时,解析 JSON 的开销会让 UI 瞬间掉帧。
  • 高级做法倒排索引(Inverted Index)

2. 方案一:引入 FlexSearch (极致性能的首选)

FlexSearch 是目前 Web 端最快的全量搜索库,它的速度比 Fuse.js 快一个数量级。

实战集成:PromptDB + FlexSearch

JavaScript

javascript 复制代码
import { Index } from "flexsearch";

class SearchablePromptDB extends PromptDB {
  constructor() {
    super();
    // 创建内存索引,开启"模糊匹配"(suggest)
    this.index = new Index({
      tokenize: "forward", // 适合前缀+中间搜索
      resolution: 9,
      cache: true
    });
  }

  // 1. 同步索引:在数据存入 DB 的同时,存入 FlexSearch
  async addWithIndex(prompt) {
    await this.set(prompt);
    this.index.add(prompt.id, `${prompt.title} ${prompt.content}`);
  }

  // 2. 毫秒级搜索
  search(query) {
    const results = this.index.search(query, { limit: 20 });
    // results 返回的是 id 数组,再去 DB 拿具体对象(或从内存缓存拿)
    return results; 
  }
}

3. 方案二:手写"倒排索引" (不依赖库的深度方案)

如果你不想增加包体积,可以利用 IndexedDB 的 multiEntry 特性。

核心技巧:词根索引化

将 Prompt 的标题和标签拆分成字/词,存入一个专门的 searchTerms 数组字段。

JavaScript

scss 复制代码
// 存入时
const prompt = {
  id: 'p1',
  title: '金融审计助手',
  // 手动分词:['金', '融', '审', '计', '助', '手', '金融', '审计']
  searchKeywords: splitWords('金融审计助手') 
};

// 在 DB 初始化时,为这个数组字段开启 multiEntry
store.createIndex('keywords_idx', 'searchKeywords', { multiEntry: true });

// 查询时
const range = IDBKeyRange.only('审计');
const request = index.getAll(range); // 瞬发响应,因为它是原生索引

注:multiEntry: true 会为数组中的每个元素在 B-Tree 中创建一个独立的指针。


4. 性能瓶颈的终极解决方案:Web Worker

即便搜索算法再快,当数据量达到 10 万级,字符串分词索引构建依然会占用主线程。

架构设计:将搜索推入边缘

  1. 主线程:只负责接收用户输入和渲染结果。
  2. Worker 线程:持有 FlexSearch 实例,监听 IndexedDB 的变化。
  3. 流程:输入 -> PostMessage -> Worker 搜索 -> 返回 ID 列表 -> 主线程渲染。

JavaScript

ini 复制代码
// search.worker.js
self.onmessage = ({ data: { type, payload } }) => {
  if (type === 'SEARCH') {
    const results = flexIndex.search(payload);
    self.postMessage(results);
  }
};

5. 优化建议

  1. 分词策略 :中文搜索最简单有效的是 "二元分词" 。例如"我的代码",拆分为 ['我', '的', '代', '码', '我的', '的代码']
  2. 防抖 (Debounce) :搜索框必须加 150-300ms 防抖,避免用户打字太快导致 Worker 任务堆积。
  3. 结果高亮 :搜索是毫秒级的,但千万别忘了在 UI 上用 <mark> 标签高亮匹配字符,这才是"体感"流畅的关键。
  4. 分片加载:搜索结果如果太多,只取前 20 条,配合滚动加载。

相关推荐
悟空瞎说18 小时前
Electron 踩坑实录:主窗口 icon 配置了,打包 Windows 后死活不显示?(全网最细排查+解决方案)
前端
Lee川18 小时前
Vue Router 4 核心精讲:从原理到面试实战
前端·vue.js
树上有只程序猿18 小时前
2026年,学“前端”还有前途吗?
前端
我命由我1234518 小时前
JS 开发问题:url.includes is not a function
开发语言·前端·javascript·html·ecmascript·html5·js
weixin1997010801618 小时前
义乌购商品详情页前端性能优化实战
前端·性能优化
汪啊汪18 小时前
Day 3:Hooks 原理
前端
汪啊汪18 小时前
Day 2:JSX 转换原理
前端
学以智用18 小时前
Vue3 + Vue Router 4 完整示例(可直接运行)
前端·vue.js
程序员小李白18 小时前
vue2基本语法详细解析(2.7条件渲染)
开发语言·前端·javascript
SuperEugene18 小时前
Vue3 项目目录结构规范:按业务域划分,新人快速上手|项目规范篇
前端·javascript·vue.js