You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中实现类Typesense的网站即时搜索功能

客户端前缀实时搜索的轻量替代方案(10K级数据)

针对你10K级客户端数据的前缀实时搜索需求,这里有几个轻量替代方案,完全不用依赖Typesense这类服务:

方案1:原生JS数组过滤(最简便)

直接利用数组的filter方法结合startsWith做前缀匹配,10K数据量下性能完全够用,不用额外构建数据结构,代码零成本。

示例代码:

// 假设加载后的JSON数据是一个键数组
const keywords = ["stranger", "strongman", "stranded", "apple", "banana"];
const inputBox = document.getElementById("search-input");
const resultList = document.getElementById("result-list");

inputBox.addEventListener("input", (e) => {
  const prefix = e.target.value.trim().toLowerCase();
  if (!prefix) {
    resultList.innerHTML = "";
    return;
  }
  // 过滤出所有以前缀开头的关键词(忽略大小写)
  const matches = keywords.filter(keyword => 
    keyword.toLowerCase().startsWith(prefix)
  );
  // 渲染结果
  resultList.innerHTML = matches.map(word => `<li>${word}</li>`).join("");
});

优点:代码极简,无需额外逻辑,适合快速实现;缺点:每次输入都要遍历整个数组,10K数据虽然没问题,但极端情况下(比如输入单个字符)会遍历全部元素,不过客户端完全能扛住。

方案2:提前构建前缀索引(优化查询速度)

如果想进一步提升查询效率,可以提前把所有可能的前缀和对应的关键词做映射,查询时直接取结果,不用遍历数组。

示例代码:

const keywords = ["stranger", "strongman", "stranded", "apple", "banana"];
const prefixIndex = {};

// 构建前缀索引
keywords.forEach(keyword => {
  const lowerKeyword = keyword.toLowerCase();
  // 生成该关键词的所有可能前缀(从1个字符到完整长度)
  for (let i = 1; i <= lowerKeyword.length; i++) {
    const prefix = lowerKeyword.slice(0, i);
    if (!prefixIndex[prefix]) {
      prefixIndex[prefix] = [];
    }
    // 去重(如果有重复关键词的话)
    if (!prefixIndex[prefix].includes(keyword)) {
      prefixIndex[prefix].push(keyword);
    }
  }
});

// 搜索逻辑
inputBox.addEventListener("input", (e) => {
  const prefix = e.target.value.trim().toLowerCase();
  const matches = prefixIndex[prefix] || [];
  resultList.innerHTML = matches.map(word => `<li>${word}</li>`).join("");
});

优点:查询时直接O(1)取结果,响应更快;缺点:构建索引需要一点初始化时间,且会占用额外内存(10K数据的话,内存开销完全可以忽略)。

方案3:Trie前缀树(专业前缀匹配结构)

如果追求最专业的前缀匹配性能,实现一个轻量Trie树,插入所有关键词后,搜索前缀时直接遍历树结构收集结果,查询效率最优。

示例代码:

// 实现一个简单的Trie树节点
class TrieNode {
  constructor() {
    this.children = {};
    this.isEnd = false;
    this.word = null; // 存储完整关键词
  }
}

class Trie {
  constructor() {
    this.root = new TrieNode();
  }

  // 插入关键词
  insert(word) {
    let node = this.root;
    const lowerWord = word.toLowerCase();
    for (const char of lowerWord) {
      if (!node.children[char]) {
        node.children[char] = new TrieNode();
      }
      node = node.children[char];
    }
    node.isEnd = true;
    node.word = word;
  }

  // 搜索前缀,返回所有匹配的关键词
  searchPrefix(prefix) {
    let node = this.root;
    const lowerPrefix = prefix.toLowerCase();
    for (const char of lowerPrefix) {
      if (!node.children[char]) {
        return []; // 没有匹配的前缀
      }
      node = node.children[char];
    }
    // 收集当前节点下所有的关键词
    const matches = [];
    this.collectWords(node, matches);
    return matches;
  }

  // 递归收集所有子节点的关键词
  collectWords(node, matches) {
    if (node.isEnd) {
      matches.push(node.word);
    }
    for (const char in node.children) {
      this.collectWords(node.children[char], matches);
    }
  }
}

// 初始化Trie树
const trie = new Trie();
keywords.forEach(word => trie.insert(word));

// 搜索逻辑
inputBox.addEventListener("input", (e) => {
  const prefix = e.target.value.trim();
  const matches = prefix ? trie.searchPrefix(prefix) : [];
  resultList.innerHTML = matches.map(word => `<li>${word}</li>`).join("");
});

优点:前缀查询效率极高,尤其是前缀较长时,性能优势明显;缺点:需要额外写Trie的实现代码,不过代码量不大,且可以复用。

方案选择建议

  • 快速实现选方案1,代码最少;
  • 追求查询速度选方案2,初始化代价小;
  • 频繁搜索或未来数据可能扩容选方案3,扩展性最好。

内容的提问来源于stack exchange,提问作者StrangerDanger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:53:13